kernel-brax3-ubuntu-touch/drivers/memory/mediatek/emi-fake-eng.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

534 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
* Copyright (c) 2021 Eric Lin <tesheng.lin@mediatek.com>
*/
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#define FAKE_ENG_EN 0x0
#define FAKE_ENG_RST 0x4
#define FAKE_ENG_DONE 0x8
#define FAKE_ENG_CON0 0xc
#define FAKE_ENG_CON1 0x10
#define FAKE_ENG_CON2 0x14
#define FAKE_ENG_CON3 0x18
#define FAKE_ENG_START_ADDR 0x1c
#define FAKE_ENG_START_ADDR_2ND 0x20
#define FAKE_ENG_ADDR 0x24
#define FAKE_ENG_INIT_PAT0 0x28
#define FAKE_ENG_INIT_PAT1 0x2c
#define FAKE_ENG_INIT_PAT2 0x30
#define FAKE_ENG_INIT_PAT3 0x34
#define FAKE_ENG_INIT_PAT4 0x38
#define FAKE_ENG_INIT_PAT5 0x3c
#define FAKE_ENG_INIT_PAT6 0x40
#define FAKE_ENG_INIT_PAT7 0x44
#define FAKE_ENG_CMP_RESULT 0x48
#define FAKE_ENG_FREEZE_RESULT 0xb0
#define FAKE_ENG_IDLE 0xb4
#define FAKE_ENG_HASH 0xb8
#define FAKE_ENG_START_ADDR_RD 0xbc
#define FAKE_ENG_START_ADDR_RD_2ND 0xc0
#define FAKE_ENG_ADDR_RD 0xc4
#define SIZE 256
struct fake_eng_set {
unsigned int chn_number;
unsigned int rd_dis;
unsigned int wr_dis;
unsigned int loop_en;
unsigned int cross_rk_en;
unsigned int data_cmp_en;
unsigned int pat_mode;
unsigned int rd_wr_joint_en;
unsigned int rd_amount;
unsigned int wr_amount;
unsigned int slow_down;
unsigned int slow_down_grp;
unsigned int burst_len;
unsigned int burst_size;
unsigned int aw_slc;
unsigned int ar_slc;
unsigned int grp_aomunt;
unsigned int start_addr_wr;
unsigned int start_addr_rd;
unsigned int start_addr_wr_2nd;
unsigned int start_addr_rd_2nd;
unsigned int start_addr_wr_extend;
unsigned int start_addr_rd_extend;
unsigned int start_addr_wr_2nd_extend;
unsigned int start_addr_rd_2nd_extend;
unsigned int addr_offset1;
unsigned int addr_offset2;
unsigned int init_pat0;
unsigned int init_pat1;
unsigned int init_pat2;
unsigned int init_pat3;
unsigned int init_pat4;
unsigned int init_pat5;
unsigned int init_pat6;
unsigned int init_pat7;
unsigned int freeze_en;
};
struct emi_fake_eng {
void __iomem **fake_eng_base;
void **k_addr;
unsigned int emi_fake_eng_cnt;
unsigned int bitmap;
struct fake_eng_set feng_arg;
};
/* global pointer for sysfs operations*/
static struct emi_fake_eng *fakeng;
static int fake_eng_init(unsigned int chn_id)
{
unsigned long phy_addr;
u32 val;
/* Basic setting for fake engine */
fakeng->feng_arg.chn_number = 0;
fakeng->feng_arg.rd_dis = 1;
fakeng->feng_arg.wr_dis = 1;
fakeng->feng_arg.loop_en = 0;
fakeng->feng_arg.cross_rk_en = 0;
fakeng->feng_arg.data_cmp_en = 0;
fakeng->feng_arg.pat_mode = 5;
fakeng->feng_arg.rd_wr_joint_en = 0;
fakeng->feng_arg.rd_amount = 30;
fakeng->feng_arg.wr_amount = 30;
fakeng->feng_arg.slow_down = 0;
fakeng->feng_arg.slow_down_grp = 0;
fakeng->feng_arg.burst_len = 7;
fakeng->feng_arg.burst_size = 4;
fakeng->feng_arg.aw_slc = 0;
fakeng->feng_arg.ar_slc = 0;
fakeng->feng_arg.grp_aomunt = 15;
fakeng->feng_arg.start_addr_wr = 0x40000000;
fakeng->feng_arg.start_addr_rd = 0x80000000;
fakeng->feng_arg.start_addr_wr_2nd = 0x40000000;
fakeng->feng_arg.start_addr_rd_2nd = 0x80000000;
fakeng->feng_arg.start_addr_wr_extend = 0x00000000;
fakeng->feng_arg.start_addr_rd_extend = 0x00000000;
fakeng->feng_arg.start_addr_wr_2nd_extend = 0x0;
fakeng->feng_arg.start_addr_rd_2nd_extend = 0x0;
fakeng->feng_arg.addr_offset1 = 0x60;
fakeng->feng_arg.addr_offset2 = 0;
fakeng->feng_arg.init_pat0 = 0x0000ffff;
fakeng->feng_arg.init_pat1 = 0x0000ffff;
fakeng->feng_arg.init_pat2 = 0x0000ffff;
fakeng->feng_arg.init_pat3 = 0x0000ffff;
fakeng->feng_arg.init_pat4 = 0xffffffff;
fakeng->feng_arg.init_pat5 = 0xffffffff;
fakeng->feng_arg.init_pat6 = 0xffffffff;
fakeng->feng_arg.init_pat7 = 0xffffffff;
fakeng->feng_arg.freeze_en = 0;
fakeng->feng_arg.chn_number = chn_id;
fakeng->k_addr[chn_id] = kzalloc(PAGE_SIZE * SIZE, GFP_KERNEL | GFP_DMA);
if (!fakeng->k_addr[chn_id])
return -ENOMEM;
phy_addr = virt_to_phys(fakeng->k_addr[chn_id]);
fakeng->feng_arg.start_addr_wr = phy_addr;
fakeng->feng_arg.start_addr_rd = phy_addr;
fakeng->feng_arg.start_addr_wr_2nd = phy_addr;
fakeng->feng_arg.start_addr_rd_2nd = phy_addr;
fakeng->feng_arg.start_addr_wr_extend = phy_addr >> 32;
fakeng->feng_arg.start_addr_rd_extend = phy_addr >> 32;
fakeng->feng_arg.start_addr_wr_2nd_extend = phy_addr >> 32;
fakeng->feng_arg.start_addr_rd_2nd_extend = phy_addr >> 32;
/* Disable fake engine*/
writel(0x0, fakeng->fake_eng_base[chn_id] + FAKE_ENG_EN);
/* Reset fake engine*/
writel(0x1, fakeng->fake_eng_base[chn_id] + FAKE_ENG_RST);
writel(0x0, fakeng->fake_eng_base[chn_id] + FAKE_ENG_RST);
/* FAKE_ENG_CON0 Mode enable control */
writel((fakeng->feng_arg.rd_wr_joint_en << 17) |
(fakeng->feng_arg.pat_mode << 13) |
(fakeng->feng_arg.data_cmp_en << 4) |
(fakeng->feng_arg.cross_rk_en << 3) |
(fakeng->feng_arg.loop_en << 2) |
(fakeng->feng_arg.wr_dis << 1) |
(fakeng->feng_arg.rd_dis << 0),
fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
/* FAKE_ENG_CON1 Command control */
writel((fakeng->feng_arg.slow_down_grp << 20) |
(fakeng->feng_arg.slow_down << 10) |
(fakeng->feng_arg.wr_amount << 5) |
(fakeng->feng_arg.rd_amount << 0),
fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON1);
/* FAKE_ENG_CON2 AXI protocal */
writel((fakeng->feng_arg.ar_slc << 19) |
(fakeng->feng_arg.aw_slc << 14) |
(fakeng->feng_arg.burst_size << 4) |
(fakeng->feng_arg.burst_len << 0),
fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON2);
/* FAKE_ENG_CON3 Command control */
writel(fakeng->feng_arg.grp_aomunt,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON3);
/* FAKE_EN_START_ADDR */
writel(fakeng->feng_arg.start_addr_wr,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_START_ADDR);
/* FAKE_EN_START_ADDR_RD */
writel(fakeng->feng_arg.start_addr_rd,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_START_ADDR_RD);
/* FAKE_EN_START_ADDR_2ND */
writel(fakeng->feng_arg.start_addr_wr_2nd,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_START_ADDR_2ND);
/* FAKE_EN_START_ADDR_RD_2ND */
writel(fakeng->feng_arg.start_addr_rd_2nd,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_START_ADDR_RD_2ND);
/* FAKE_ENG_ADDR */
writel((fakeng->feng_arg.addr_offset2 << 18) |
(fakeng->feng_arg.addr_offset1 << 8) |
(fakeng->feng_arg.start_addr_wr_2nd_extend << 4) |
(fakeng->feng_arg.start_addr_wr_extend << 0),
fakeng->fake_eng_base[chn_id] + FAKE_ENG_ADDR);
/* FAKE_ENG_ADDR_RD */
writel((fakeng->feng_arg.start_addr_rd_2nd_extend << 4) |
(fakeng->feng_arg.start_addr_rd_extend << 0),
fakeng->fake_eng_base[chn_id] + FAKE_ENG_ADDR_RD);
/* INIT_PAT0 */
writel(fakeng->feng_arg.init_pat0,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT0);
/* INIT_PAT1 */
writel(fakeng->feng_arg.init_pat1,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT1);
/* INIT_PAT2 */
writel(fakeng->feng_arg.init_pat2,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT2);
/* INIT_PAT3 */
writel(fakeng->feng_arg.init_pat3,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT3);
/* INIT_PAT4 */
writel(fakeng->feng_arg.init_pat4,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT4);
/* INIT_PAT5 */
writel(fakeng->feng_arg.init_pat5,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT5);
/* INIT_PAT6 */
writel(fakeng->feng_arg.init_pat6,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT6);
/* INIT_PAT7 */
writel(fakeng->feng_arg.init_pat7,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_INIT_PAT7);
/* compare result freeze */
writel(fakeng->feng_arg.freeze_en,
fakeng->fake_eng_base[chn_id] + FAKE_ENG_FREEZE_RESULT);
/* HASH */
val = (0x0 << 24) | (0x1 << 20) | (0x1 << 16) | (0x2 << 12) |
(0x1 << 8) | (fakeng->feng_arg.chn_number << 4) | 0x1;
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_HASH);
return 0;
}
static void emi_fake_eng_stop(unsigned int chn_id)
{
unsigned int val;
if (!(fakeng->bitmap & (0x1 << chn_id))) {
pr_info("%s: fake eng:%d no start, do nothing here\n", __func__, chn_id);
return;
}
/* set loop_en = 0 */
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
val &= ~(0x1 << 2);
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
/* wait for loop mode disable*/
udelay(5);
/* Wait fake engine done */
while (1) {
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_DONE);
pr_debug_ratelimited("%s: fake eng %d, val: %d\n",
__func__, chn_id, val);
if (val)
break;
}
/* Disable fake engine */
writel(0x0, fakeng->fake_eng_base[chn_id] + FAKE_ENG_EN);
/* Reset fake engine */
writel(0x1, fakeng->fake_eng_base[chn_id] + FAKE_ENG_RST);
writel(0x0, fakeng->fake_eng_base[chn_id] + FAKE_ENG_RST);
/* free k_addr */
kfree(fakeng->k_addr[chn_id]);
/* Clear chn_id */
fakeng->bitmap &= ~(0x1 << chn_id);
pr_info("%s: disable fake eng:%d done, fake eng bitmap:0x%x\n",
__func__, chn_id, fakeng->bitmap);
}
static void emi_fake_eng_start(unsigned int chn_id,
unsigned int wr_en,
unsigned int rd_en,
unsigned int latency)
{
u32 loop_en = 1;
u32 slow_down, slow_down_grp, val;
int ret;
if (fakeng->bitmap & (0x1 << chn_id)) {
pr_info("%s:Please disable fake eng:%d first\n", __func__, chn_id);
return;
}
/* set chn_id bitmap */
fakeng->bitmap |= (0x1 << chn_id);
/* Fake engine basic setting */
ret = fake_eng_init(chn_id);
if (ret) {
pr_info("%s: Cannot allocate memory, stop trigger fake engine\n", __func__);
return;
}
/* set slow_down */
slow_down = latency;
/* Start to incease BW, */
slow_down_grp = slow_down;
if (rd_en) {
/* set rd_dis = 0 to enable read command*/
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
val &= ~(0x1 << 0);
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
}
if (wr_en) {
/* set wr_dis = 0 to enable write command*/
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
val &= ~(0x1 << 1);
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
}
/* set slow_down = 0 */
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON1);
val &= ~(0xfffff << 10);
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON1);
/* set slow_down */
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON1);
val |= (slow_down_grp << 20) | (slow_down << 10);
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON1);
/* set loop_en = 1 */
val = readl(fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
val |= (loop_en << 2);
writel(val, fakeng->fake_eng_base[chn_id] + FAKE_ENG_CON0);
/* Enable fake engine */
writel(0x1, fakeng->fake_eng_base[chn_id] + FAKE_ENG_EN);
pr_info("%s: chn_id:%d, wr_en:%d, rd_en:%d, latency:%d\n",
__func__, chn_id, wr_en, rd_en, slow_down);
pr_info("%s: fake eng bitmap:0x%x\n", __func__, fakeng->bitmap);
}
static ssize_t emi_fake_eng_show(struct device_driver *driver, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "0x%x\n", fakeng->bitmap);
}
static ssize_t emi_fake_eng_store
(struct device_driver *driver, const char *buf, size_t count)
{
unsigned int chn_id, en, wr_en, rd_en, latency;
int ret = 0, n;
n = sscanf(buf, "%u,%u,%u,%u,%u\n",
&chn_id, &en, &wr_en, &rd_en, &latency);
if (n != 5) {
pr_info("%s: Please enter correct input format. n=%d\n", __func__, n);
goto out;
}
pr_info("%s: %u %u %u %u %u\n",
__func__, chn_id, en, wr_en, rd_en, latency);
if (chn_id >= fakeng->emi_fake_eng_cnt) {
pr_info("%s: Please enter channel number between 0 ~ %d\n",
__func__, fakeng->emi_fake_eng_cnt - 1);
goto out;
}
if (en > 1 || wr_en > 1 || rd_en > 1) {
pr_info("%s: Please enter en, wr_en, rd_en value for 0 or 1\n", __func__);
goto out;
}
if (wr_en == 0 && rd_en == 0) {
pr_info("%s: At least one of wr_en or rd_en should be 1\n", __func__);
goto out;
}
if (latency > 100) {
pr_info("%s: Please enter latency value between 1 ~ 100\n", __func__);
goto out;
}
if (en)
emi_fake_eng_start(chn_id, wr_en, rd_en, latency);
else
emi_fake_eng_stop(chn_id);
out:
return ret ? : count;
}
static DRIVER_ATTR_RW(emi_fake_eng);
static int emi_fake_eng_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "driver removed\n");
fakeng = NULL;
return 0;
}
static int emi_fake_eng_probe(struct platform_device *pdev)
{
struct device_node *emi_feng_node = pdev->dev.of_node;
struct emi_fake_eng *feng;
unsigned int i;
int ret;
dev_info(&pdev->dev, "driver probed\n");
feng = devm_kzalloc(&pdev->dev, sizeof(struct emi_fake_eng), GFP_KERNEL);
if (!feng)
return -ENOMEM;
ret = of_property_count_elems_of_size(emi_feng_node,
"reg", sizeof(unsigned int) * 4);
if (ret <= 0) {
dev_err(&pdev->dev, "No reg\n");
return -ENXIO;
}
feng->emi_fake_eng_cnt = (unsigned int)ret;
feng->fake_eng_base = devm_kmalloc_array(&pdev->dev,
feng->emi_fake_eng_cnt, sizeof(phys_addr_t), GFP_KERNEL);
if (!(feng->fake_eng_base))
return -ENOMEM;
for (i = 0; i < feng->emi_fake_eng_cnt; i++) {
feng->fake_eng_base[i] = of_iomap(emi_feng_node, i);
if (!feng->fake_eng_base[i])
return -ENOMEM;
}
feng->k_addr = devm_kmalloc_array(&pdev->dev,
feng->emi_fake_eng_cnt, sizeof(void *), GFP_KERNEL);
if (!(feng->k_addr))
return -ENOMEM;
/* Set to global pointer */
fakeng = feng;
/* Initial channel bitmap */
fakeng->bitmap = 0;
return 0;
}
static const struct of_device_id emi_fake_eng_of_ids[] = {
{.compatible = "mediatek,emi-fake-engine",},
{}
};
static struct platform_driver emi_fake_eng_drv = {
.probe = emi_fake_eng_probe,
.remove = emi_fake_eng_remove,
.driver = {
.name = "emi_fake_eng_drv",
.owner = THIS_MODULE,
.of_match_table = emi_fake_eng_of_ids,
},
};
static void __exit emi_fake_eng_exit(void)
{
pr_info("emi fake engine unloaded\n");
driver_remove_file(&emi_fake_eng_drv.driver,
&driver_attr_emi_fake_eng);
platform_driver_unregister(&emi_fake_eng_drv);
}
static int __init emi_fake_eng_init(void)
{
int ret;
pr_info("emi fake engine loaded\n");
ret = platform_driver_register(&emi_fake_eng_drv);
if (ret) {
pr_info("emi fake engine: failed to register dirver\n");
return ret;
}
ret = driver_create_file(&emi_fake_eng_drv.driver,
&driver_attr_emi_fake_eng);
if (ret) {
pr_info("emi fake engine: failed to create control file\n");
return ret;
}
return 0;
}
module_init(emi_fake_eng_init);
module_exit(emi_fake_eng_exit);
MODULE_DESCRIPTION("MediaTek EMI Fake Engine Driver");
MODULE_LICENSE("GPL v2");