unplugged-kernel/drivers/misc/mediatek/emi_bwl/mt6739/emi_bwl.c

526 lines
13 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/semaphore.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/printk.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#define MET_USER_EVENT_SUPPORT
/* #include <linux/met_drv.h> */
#include <mt-plat/mtk_io.h>
#include <mt-plat/sync_write.h>
/* #include <ext_wd_drv.h> */
#include "emi_bwl.h"
#include "emi_mbw.h"
#include "emi_elm.h"
#define DRIVER_ATTR(_name, _mode, _show, _store) \
struct driver_attribute driver_attr_##_name = \
__ATTR(_name, _mode, _show, _store)
DEFINE_SEMAPHORE(emi_bwl_sem);
static void __iomem *CEN_EMI_BASE;
static void __iomem *CHA_EMI_BASE;
static void __iomem *EMI_MPU_BASE;
static unsigned int mpu_irq;
static unsigned int cgm_irq;
static unsigned int elm_irq;
static struct emi_info_t emi_info;
static int emi_probe(struct platform_device *pdev)
{
struct resource *res;
struct device_node *node = pdev->dev.of_node;
int ret;
pr_debug("[EMI] module probe.\n");
if (node) {
mpu_irq = irq_of_parse_and_map(node, 0);
cgm_irq = irq_of_parse_and_map(node, 1);
elm_irq = irq_of_parse_and_map(node, 2);
pr_info("[EMI] get irq of MPU(%d), GCM(%d), ELM(%d)\n",
mpu_irq, cgm_irq, elm_irq);
} else {
mpu_irq = 0;
cgm_irq = 0;
elm_irq = 0;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
CEN_EMI_BASE = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(CEN_EMI_BASE)) {
pr_info("[EMI] unable to map CEN_EMI_BASE\n");
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
CHA_EMI_BASE = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(CHA_EMI_BASE)) {
pr_info("[EMI] unable to map CHA_EMI_BASE\n");
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
EMI_MPU_BASE = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(EMI_MPU_BASE)) {
pr_info("[EMI] unable to map EMI_MPU_BASE\n");
return -EINVAL;
}
pr_info("[EMI] get CEN_EMI_BASE @ %p\n", mt_cen_emi_base_get());
pr_info("[EMI] get CHA_EMI_BASE @ %p\n", mt_chn_emi_base_get());
pr_info("[EMI] get EMI_MPU_BASE @ %p\n", mt_emi_mpu_base_get());
ret = mtk_mem_bw_ctrl(CON_SCE_UI, ENABLE_CON_SCE);
if (ret)
pr_info("[EMI/BWL] fail to set EMI bandwidth limiter\n");
writel(0x00000040, CHA_EMI_BASE+0x0008);
mt_reg_sync_writel(0x00000913, CEN_EMI_BASE+0x5B0);
#if ENABLE_MBW
mbw_init();
#endif
#if ENABLE_ELM
elm_init(cgm_irq);
#endif
return 0;
}
static int emi_remove(struct platform_device *dev)
{
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id emi_of_ids[] = {
{.compatible = "mediatek,emi",},
{}
};
#endif
#ifdef CONFIG_PM
static int emi_suspend_noirq(struct device *dev)
{
/* pr_info("[EMI] suspend\n"); */
suspend_elm();
return 0;
}
static int emi_resume_noirq(struct device *dev)
{
/* pr_info("[EMI] resume\n"); */
resume_elm();
return 0;
}
static const struct dev_pm_ops emi_pm_ops = {
.suspend_noirq = emi_suspend_noirq,
.resume_noirq = emi_resume_noirq,
};
#define EMI_PM_OPS (&emi_pm_ops)
#else
#define EMI_PM_OPS NULL
#endif
static struct platform_driver emi_ctrl = {
.probe = emi_probe,
.remove = emi_remove,
.driver = {
.name = "emi_ctrl",
.owner = THIS_MODULE,
.pm = EMI_PM_OPS,
#ifdef CONFIG_OF
.of_match_table = emi_of_ids,
#endif
},
};
/* define EMI bandwiwth limiter control table */
static struct emi_bwl_ctrl ctrl_tbl[NR_CON_SCE];
/* current concurrency scenario */
static int cur_con_sce = 0x0FFFFFFF;
/* define concurrency scenario strings */
static const char * const con_sce_str[] = {
#define X_CON_SCE(con_sce, arba, arbb, arbc, arbd, arbe, arbf, arbg, arbh, \
conm, mdct) (#con_sce),
#include "con_sce_lpddr3.h"
#undef X_CON_SCE
};
/****************** For LPDDR4-3200******************/
static const unsigned int emi_arba_lpddr4_val[] = {
#define X_CON_SCE(con_sce, arba, arbb, arbc, arbd, arbe, arbf, arbg, arbh, \
conm, mdct) arba,
#include "con_sce_lpddr3.h"
#undef X_CON_SCE
};
static const unsigned int emi_arbb_lpddr4_val[] = {
#define X_CON_SCE(con_sce, arba, arbb, arbc, arbd, arbe, arbf, arbg, arbh, \
conm, mdct) arbb,
#include "con_sce_lpddr3.h"
#undef X_CON_SCE
};
static const unsigned int emi_arbc_lpddr4_val[] = {
#define X_CON_SCE(con_sce, arba, arbb, arbc, arbd, arbe, arbf, arbg, arbh, \
conm, mdct) arbc,
#include "con_sce_lpddr3.h"
#undef X_CON_SCE
};
static const unsigned int emi_arbd_lpddr4_val[] = {
#define X_CON_SCE(con_sce, arba, arbb, arbc, arbd, arbe, arbf, arbg, arbh, \
conm, mdct) arbd,
#include "con_sce_lpddr3.h"
#undef X_CON_SCE
};
static const unsigned int emi_arbe_lpddr4_val[] = {
#define X_CON_SCE(con_sce, arba, arbb, arbc, arbd, arbe, arbf, arbg, arbh, \
conm, mdct) arbe,
#include "con_sce_lpddr3.h"
#undef X_CON_SCE
};
static const unsigned int emi_arbf_lpddr4_val[] = {
#define X_CON_SCE(con_sce, arba, arbb, arbc, arbd, arbe, arbf, arbg, arbh, \
conm, mdct) arbf,
#include "con_sce_lpddr3.h"
#undef X_CON_SCE
};
static const unsigned int emi_arbg_lpddr4_val[] = {
#define X_CON_SCE(con_sce, arba, arbb, arbc, arbd, arbe, arbf, arbg, arbh, \
conm, mdct) arbg,
#include "con_sce_lpddr3.h"
#undef X_CON_SCE
};
static const unsigned int emi_arbh_lpddr4_val[] = {
#define X_CON_SCE(con_sce, arba, arbb, arbc, arbd, arbe, arbf, arbg, arbh, \
conm, mdct) arbh,
#include "con_sce_lpddr3.h"
#undef X_CON_SCE
};
static const unsigned int emi_conm_lpddr4_val[] = {
#define X_CON_SCE(con_sce, arba, arbb, arbc, arbd, arbe, arbf, arbg, arbh, \
conm, mdct) conm,
#include "con_sce_lpddr3.h"
#undef X_CON_SCE
};
static const unsigned int emi_mdct_lpddr4_val[] = {
#define X_CON_SCE(con_sce, arba, arbb, arbc, arbd, arbe, arbf, arbg, arbh, \
conm, mdct) mdct,
#include "con_sce_lpddr3.h"
#undef X_CON_SCE
};
/*
* mtk_mem_bw_ctrl: set EMI bandwidth limiter for memory bandwidth control
* @sce: concurrency scenario ID
* @op: either ENABLE_CON_SCE or DISABLE_CON_SCE
* Return 0 for success; return negative values for failure.
*/
int mtk_mem_bw_ctrl(int sce, int op)
{
int i, highest;
if (sce >= NR_CON_SCE)
return -1;
if (op != ENABLE_CON_SCE && op != DISABLE_CON_SCE)
return -1;
if (in_interrupt())
return -1;
down(&emi_bwl_sem);
if (op == ENABLE_CON_SCE)
ctrl_tbl[sce].ref_cnt++;
else if (op == DISABLE_CON_SCE) {
if (ctrl_tbl[sce].ref_cnt != 0)
ctrl_tbl[sce].ref_cnt--;
}
/* find the scenario with the highest priority */
highest = -1;
for (i = 0; i < NR_CON_SCE; i++) {
if (ctrl_tbl[i].ref_cnt != 0) {
highest = i;
break;
}
}
if (highest == -1)
highest = CON_SCE_UI;
/* set new EMI bandwidth limiter value */
if (highest != cur_con_sce) {
writel(emi_arba_lpddr4_val[highest], EMI_ARBA);
writel(emi_arbb_lpddr4_val[highest], EMI_ARBB);
writel(emi_arbc_lpddr4_val[highest], EMI_ARBC);
writel(emi_arbd_lpddr4_val[highest], EMI_ARBD);
writel(emi_arbe_lpddr4_val[highest], EMI_ARBE);
writel(emi_arbf_lpddr4_val[highest], EMI_ARBF);
writel(emi_arbg_lpddr4_val[highest], EMI_ARBG);
writel(emi_arbh_lpddr4_val[highest], EMI_ARBH);
writel(emi_conm_lpddr4_val[highest], EMI_CONM);
mt_reg_sync_writel(emi_mdct_lpddr4_val[highest], EMI_MDCT);
cur_con_sce = highest;
}
up(&emi_bwl_sem);
return 0;
}
/*
* con_sce_show: sysfs con_sce file show function.
* @driver:
* @buf:
* Return the number of read bytes.
*/
static ssize_t con_sce_show(struct device_driver *driver, char *buf)
{
char *ptr = buf;
int i = 0;
if (cur_con_sce >= NR_CON_SCE) {
ptr += sprintf(ptr, "none\n");
return strlen(buf);
}
ptr += snprintf(ptr, 64, "current scenario: %s\n", con_sce_str[cur_con_sce]);
#if 1
ptr += snprintf(ptr, 32, "%s\n", con_sce_str[cur_con_sce]);
ptr += sprintf(ptr, "EMI_ARBA = 0x%x\n", readl(IOMEM(EMI_ARBA)));
ptr += sprintf(ptr, "EMI_ARBB = 0x%x\n", readl(IOMEM(EMI_ARBB)));
ptr += sprintf(ptr, "EMI_ARBC = 0x%x\n", readl(IOMEM(EMI_ARBC)));
ptr += sprintf(ptr, "EMI_ARBD = 0x%x\n", readl(IOMEM(EMI_ARBD)));
ptr += sprintf(ptr, "EMI_ARBE = 0x%x\n", readl(IOMEM(EMI_ARBE)));
ptr += sprintf(ptr, "EMI_ARBF = 0x%x\n", readl(IOMEM(EMI_ARBF)));
ptr += sprintf(ptr, "EMI_ARBG = 0x%x\n", readl(IOMEM(EMI_ARBG)));
ptr += sprintf(ptr, "EMI_ARBH = 0x%x\n", readl(IOMEM(EMI_ARBH)));
ptr += sprintf(ptr, "EMI_CONM = 0x%x\n", readl(IOMEM(EMI_CONM)));
ptr += sprintf(ptr, "EMI_MDCT = 0x%x\n", readl(IOMEM(EMI_MDCT)));
for (i = 0; i < NR_CON_SCE; i++)
ptr += snprintf(ptr, 64, "%s = 0x%x\n",
con_sce_str[i], ctrl_tbl[i].ref_cnt);
pr_debug("[EMI BWL] EMI_ARBA = 0x%x\n", readl(IOMEM(EMI_ARBA)));
pr_debug("[EMI BWL] EMI_ARBB = 0x%x\n", readl(IOMEM(EMI_ARBB)));
pr_debug("[EMI BWL] EMI_ARBC = 0x%x\n", readl(IOMEM(EMI_ARBC)));
pr_debug("[EMI BWL] EMI_ARBD = 0x%x\n", readl(IOMEM(EMI_ARBD)));
pr_debug("[EMI BWL] EMI_ARBE = 0x%x\n", readl(IOMEM(EMI_ARBE)));
pr_debug("[EMI BWL] EMI_ARBF = 0x%x\n", readl(IOMEM(EMI_ARBF)));
pr_debug("[EMI BWL] EMI_ARBG = 0x%x\n", readl(IOMEM(EMI_ARBG)));
pr_debug("[EMI BWL] EMI_ARBH = 0x%x\n", readl(IOMEM(EMI_ARBH)));
pr_debug("[EMI BWL] EMI_CONM = 0x%x\n", readl(IOMEM(EMI_CONM)));
pr_debug("[EMI BWL] EMI_MDCT = 0x%x\n", readl(IOMEM(EMI_MDCT)));
#endif
return strlen(buf);
}
/*
* con_sce_store: sysfs con_sce file store function.
* @driver:
* @buf:
* @count:
* Return the number of write bytes.
*/
static ssize_t con_sce_store(struct device_driver *driver,
const char *buf, size_t count)
{
int i;
for (i = 0; i < NR_CON_SCE; i++) {
if (!strncmp(buf, con_sce_str[i], strlen(con_sce_str[i]))) {
if (!strncmp(buf + strlen(con_sce_str[i]) + 1,
EN_CON_SCE_STR, strlen(EN_CON_SCE_STR))) {
mtk_mem_bw_ctrl(i, ENABLE_CON_SCE);
/* pr_debug("concurrency scenario %s ON\n", con_sce_str[i]); */
break;
} else if (!strncmp(buf + strlen(con_sce_str[i]) + 1,
DIS_CON_SCE_STR, strlen(DIS_CON_SCE_STR))) {
mtk_mem_bw_ctrl(i, DISABLE_CON_SCE);
/* pr_debug("concurrency scenario %s OFF\n", con_sce_str[i]); */
break;
}
}
}
return count;
}
DRIVER_ATTR(concurrency_scenario, 0644, con_sce_show, con_sce_store);
static ssize_t elm_ctrl_show(struct device_driver *driver, char *buf)
{
char *ptr;
ptr = (char *)buf;
ptr += sprintf(ptr, "ELM enabled: %d\n", is_elm_enabled());
return strlen(buf);
}
static ssize_t elm_ctrl_store(struct device_driver *driver,
const char *buf, size_t count)
{
if (!strncmp(buf, "ON", strlen("ON")))
enable_elm();
else if (!strncmp(buf, "OFF", strlen("OFF")))
disable_elm();
return count;
}
DRIVER_ATTR(elm_ctrl, 0644, elm_ctrl_show, elm_ctrl_store);
/*
* emi_ctrl_init: module init function.
*/
static int __init emi_ctrl_init(void)
{
int ret;
int i;
/* register EMI ctrl interface */
ret = platform_driver_register(&emi_ctrl);
if (ret)
pr_info("[EMI/BWL] fail to register emi_ctrl driver\n");
ret = driver_create_file(&emi_ctrl.driver, &driver_attr_concurrency_scenario);
if (ret)
pr_info("[EMI/BWL] fail to create emi_bwl sysfs file\n");
ret = driver_create_file(&emi_ctrl.driver, &driver_attr_elm_ctrl);
if (ret)
pr_info("[EMI/ELM] fail to create elm_ctrl file\n");
/* get EMI info from boot tags */
if (of_chosen) {
ret = of_property_read_u32(of_chosen, "emi_info,dram_type", &(emi_info.dram_type));
if (ret)
pr_info("[EMI] fail to get dram_type\n");
ret = of_property_read_u32(of_chosen, "emi_info,ch_num", &(emi_info.ch_num));
if (ret)
pr_info("[EMI] fail to get ch_num\n");
ret = of_property_read_u32(of_chosen, "emi_info,rk_num", &(emi_info.rk_num));
if (ret)
pr_info("[EMI] fail to get rk_num\n");
ret = of_property_read_u32_array(of_chosen, "emi_info,rank_size",
emi_info.rank_size, MAX_RK);
if (ret)
pr_info("[EMI] fail to get rank_size\n");
}
pr_info("[EMI] dram_type(%d)\n", get_dram_type());
pr_info("[EMI] ch_num(%d)\n", get_ch_num());
pr_info("[EMI] rk_num(%d)\n", get_rk_num());
for (i = 0; i < get_rk_num(); i++)
pr_info("[EMI] rank%d_size(0x%x)", i, get_rank_size(i));
return 0;
}
/*
* emi_ctrl_exit: module exit function.
*/
static void __exit emi_ctrl_exit(void)
{
}
postcore_initcall(emi_ctrl_init);
module_exit(emi_ctrl_exit);
unsigned int get_ch_num(void)
{
return emi_info.ch_num;
}
unsigned int get_rk_num(void)
{
if (emi_info.rk_num > MAX_RK)
pr_info("[EMI] rank overflow\n");
return emi_info.rk_num;
}
unsigned int get_rank_size(unsigned int rank_index)
{
if (rank_index < emi_info.rk_num)
return emi_info.rank_size[rank_index];
return 0;
}
unsigned int get_dram_type(void)
{
return emi_info.dram_type;
}
EXPORT_SYMBOL(get_dram_type);
void __iomem *mt_cen_emi_base_get(void)
{
return CEN_EMI_BASE;
}
EXPORT_SYMBOL(mt_cen_emi_base_get);
void __iomem *mt_emi_base_get(void)
{
return mt_cen_emi_base_get();
}
EXPORT_SYMBOL(mt_emi_base_get);
void __iomem *mt_chn_emi_base_get()
{
return CHA_EMI_BASE;
}
EXPORT_SYMBOL(mt_chn_emi_base_get);
void __iomem *mt_emi_mpu_base_get(void)
{
return EMI_MPU_BASE;
}
EXPORT_SYMBOL(mt_emi_mpu_base_get);
unsigned int mt_emi_mpu_irq_get(void)
{
return mpu_irq;
}
unsigned int mt_emi_elm_irq_get(void)
{
return elm_irq;
}
unsigned int mt_emi_cgm_irq_get(void)
{
return cgm_irq;
}