367 lines
9.5 KiB
C
367 lines
9.5 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/of_irq.h>
|
|
#include <mt-plat/sync_write.h>
|
|
#include <mt-plat/mtk_io.h>
|
|
#ifdef CONFIG_MTK_AEE_FEATURE
|
|
#include <mt-plat/aee.h>
|
|
#endif
|
|
|
|
#include "emi_elm.h"
|
|
#include "emi_bwl.h"
|
|
#include "emi_mbw.h"
|
|
#include <mtk_dramc.h>
|
|
|
|
static void __iomem *CEN_EMI_BASE; /* not initialise statics to 0 or NULL */
|
|
static bool elm_enabled;
|
|
static DEFINE_SPINLOCK(elm_lock);
|
|
static unsigned int elm_valid;
|
|
static unsigned int dram_data_rate;
|
|
static unsigned int emi_cgma_st0;
|
|
static unsigned int emi_cgma_st1;
|
|
static unsigned int emi_cgma_st2;
|
|
static unsigned int emi_ebmint_st;
|
|
static unsigned int emi_ltct0;
|
|
static unsigned int emi_ltct1;
|
|
static unsigned int emi_ltct2;
|
|
static unsigned int emi_ltct3;
|
|
static unsigned int emi_ltst0;
|
|
static unsigned int emi_ltst1;
|
|
static unsigned int emi_ltst2;
|
|
static unsigned int emi_bmen;
|
|
static unsigned int emi_bmen2;
|
|
static unsigned int emi_msel;
|
|
static unsigned int emi_msel2;
|
|
static unsigned int emi_bmrw0;
|
|
static unsigned int emi_bcnt;
|
|
static unsigned int emi_wsct;
|
|
static unsigned int emi_wsct2;
|
|
static unsigned int emi_wsct3;
|
|
static unsigned int emi_wsct4;
|
|
static unsigned int emi_ttype[16];
|
|
|
|
bool is_elm_enabled(void)
|
|
{
|
|
return elm_enabled;
|
|
}
|
|
EXPORT_SYMBOL(is_elm_enabled);
|
|
|
|
void enable_elm(void)
|
|
{
|
|
unsigned long flags;
|
|
int emi_dcm_status;
|
|
|
|
spin_lock_irqsave(&elm_lock, flags);
|
|
emi_dcm_status = BM_GetEmiDcm();
|
|
BM_SetEmiDcm(0x40);
|
|
|
|
/* Setup and enable BM */
|
|
mt_reg_sync_writel(readl(IOMEM(EMI_BMEN)) & 0xfffffffc, EMI_BMEN);
|
|
writel(0x00ff0000, IOMEM(EMI_BMEN));
|
|
writel(0x00020001, IOMEM(EMI_MSEL));
|
|
writel(0x00000018, IOMEM(EMI_MSEL2));
|
|
writel(0x02000000, IOMEM(EMI_BMEN2));
|
|
writel(0xffffffff, IOMEM(EMI_BMRW0));
|
|
mt_reg_sync_writel(readl(IOMEM(EMI_BMEN)) | 0x1, EMI_BMEN);
|
|
|
|
/* Setup and enable CGM */
|
|
writel(0x0ffffff0, IOMEM(EMI_CGMA));
|
|
|
|
/* Enable ELM */
|
|
writel(readl(IOMEM(EMI_LTCT0_2ND)) | 0x8, IOMEM(EMI_LTCT0_2ND));
|
|
mt_reg_sync_writel(readl(IOMEM(EMI_LTCT0_2ND)) | 0x80000000, EMI_LTCT0_2ND);
|
|
mt_reg_sync_writel(readl(IOMEM(EMI_LTCT0_2ND)) | 0x1, EMI_LTCT0_2ND);
|
|
|
|
elm_enabled = true;
|
|
|
|
BM_SetEmiDcm(emi_dcm_status);
|
|
spin_unlock_irqrestore(&elm_lock, flags);
|
|
}
|
|
EXPORT_SYMBOL(enable_elm);
|
|
|
|
void disable_elm(void)
|
|
{
|
|
unsigned long flags;
|
|
int emi_dcm_status;
|
|
|
|
spin_lock_irqsave(&elm_lock, flags);
|
|
emi_dcm_status = BM_GetEmiDcm();
|
|
BM_SetEmiDcm(0x40);
|
|
|
|
/* Clear BM */
|
|
mt_reg_sync_writel(readl(IOMEM(EMI_BMEN)) & 0xfffffffc, EMI_BMEN);
|
|
|
|
/* Disable CGM */
|
|
writel(0x0, IOMEM(EMI_CGMA));
|
|
|
|
/* Disable ELM */
|
|
writel(readl(IOMEM(EMI_LTCT0_2ND)) & ~0x1, IOMEM(EMI_LTCT0_2ND));
|
|
mt_reg_sync_writel(readl(IOMEM(EMI_LTCT0_2ND)) & ~0x8, EMI_LTCT0_2ND);
|
|
|
|
elm_enabled = false;
|
|
|
|
BM_SetEmiDcm(emi_dcm_status);
|
|
spin_unlock_irqrestore(&elm_lock, flags);
|
|
}
|
|
EXPORT_SYMBOL(disable_elm);
|
|
|
|
static void lt_init(void)
|
|
{
|
|
/* Ask EMI DE to provide the setting of EMI_LTCT0~3,
|
|
* which control the period and the threshold of ELM
|
|
*/
|
|
writel(0x1000cc08, IOMEM(EMI_LTCT0_2ND));
|
|
writel(0x08230823, IOMEM(EMI_LTCT1_2ND));
|
|
writel(0x08230682, IOMEM(EMI_LTCT2_2ND));
|
|
mt_reg_sync_writel(0x362B3636, EMI_LTCT3_2ND);
|
|
|
|
enable_elm();
|
|
}
|
|
|
|
static int lt_reg_save(void)
|
|
{
|
|
unsigned int i;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&elm_lock, flags);
|
|
dram_data_rate = get_dram_data_rate();
|
|
|
|
emi_cgma_st0 = readl(IOMEM(EMI_CGMA_ST0));
|
|
emi_cgma_st1 = readl(IOMEM(EMI_CGMA_ST1));
|
|
emi_cgma_st2 = readl(IOMEM(EMI_CGMA_ST2));
|
|
emi_ebmint_st = readl(IOMEM(EMI_EBMINT_ST));
|
|
|
|
emi_ltct0 = readl(IOMEM(EMI_LTCT0_2ND));
|
|
emi_ltct1 = readl(IOMEM(EMI_LTCT1_2ND));
|
|
emi_ltct2 = readl(IOMEM(EMI_LTCT2_2ND));
|
|
emi_ltct3 = readl(IOMEM(EMI_LTCT3_2ND));
|
|
emi_ltst0 = readl(IOMEM(EMI_LTST0_2ND));
|
|
emi_ltst1 = readl(IOMEM(EMI_LTST1_2ND));
|
|
emi_ltst2 = readl(IOMEM(EMI_LTST2_2ND));
|
|
|
|
emi_bmen = readl(IOMEM(EMI_BMEN));
|
|
emi_bmen2 = readl(IOMEM(EMI_BMEN2));
|
|
emi_msel = readl(IOMEM(EMI_MSEL));
|
|
emi_msel2 = readl(IOMEM(EMI_MSEL2));
|
|
emi_bmrw0 = readl(IOMEM(EMI_BMRW0));
|
|
emi_bcnt = readl(IOMEM(EMI_BCNT));
|
|
emi_wsct = readl(IOMEM(EMI_WSCT));
|
|
emi_wsct2 = readl(IOMEM(EMI_WSCT2));
|
|
emi_wsct3 = readl(IOMEM(EMI_WSCT3));
|
|
emi_wsct4 = readl(IOMEM(EMI_WSCT4));
|
|
|
|
for (i = 0; i < 16; i++)
|
|
emi_ttype[i] = readl(IOMEM(EMI_TTYPE(i)));
|
|
|
|
elm_valid = 1;
|
|
spin_unlock_irqrestore(&elm_lock, flags);
|
|
|
|
#if 0
|
|
pr_info(
|
|
"[EMI ELM] ddr data rate: %d, valid: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n",
|
|
dram_data_rate, elm_valid,
|
|
"EMI_CGMA_ST0", emi_cgma_st0,
|
|
"EMI_CGMA_ST1", emi_cgma_st1,
|
|
"EMI_CGMA_ST2", emi_cgma_st2,
|
|
"EMI_EBMINT_ST", emi_ebmint_st,
|
|
"EMI_LTCT0", emi_ltct0,
|
|
"EMI_LTCT1", emi_ltct1,
|
|
"EMI_LTCT2", emi_ltct2,
|
|
"EMI_LTCT3", emi_ltct3,
|
|
"EMI_LTST0", emi_ltst0,
|
|
"EMI_LTST1", emi_ltst1,
|
|
"EMI_LTST2", emi_ltst2,
|
|
"EMI_BMEN", emi_bmen,
|
|
"EMI_BMEN2", emi_bmen2,
|
|
"EMI_MSEL", emi_msel,
|
|
"EMI_MSEL2", emi_msel2,
|
|
"EMI_BMRW0", emi_bmrw0,
|
|
"EMI_BCNT", emi_bcnt,
|
|
"EMI_WSCT", emi_wsct,
|
|
"EMI_WSCT2", emi_wsct2,
|
|
"EMI_WSCT3", emi_wsct3,
|
|
"EMI_WSCT4", emi_wsct4,
|
|
"EMI_TTYPE1", emi_ttype[0],
|
|
"EMI_TTYPE2", emi_ttype[1],
|
|
"EMI_TTYPE3", emi_ttype[2],
|
|
"EMI_TTYPE4", emi_ttype[3],
|
|
"EMI_TTYPE5", emi_ttype[4],
|
|
"EMI_TTYPE6", emi_ttype[5],
|
|
"EMI_TTYPE7", emi_ttype[6],
|
|
"EMI_TTYPE8", emi_ttype[7],
|
|
"EMI_TTYPE9", emi_ttype[8],
|
|
"EMI_TTYPE10", emi_ttype[9],
|
|
"EMI_TTYPE11", emi_ttype[10],
|
|
"EMI_TTYPE12", emi_ttype[11],
|
|
"EMI_TTYPE13", emi_ttype[12],
|
|
"EMI_TTYPE14", emi_ttype[13],
|
|
"EMI_TTYPE15", emi_ttype[14],
|
|
"EMI_TTYPE16", emi_ttype[15]
|
|
);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void elm_dump(char *buf, unsigned int leng)
|
|
{
|
|
unsigned long flags;
|
|
int err;
|
|
|
|
spin_lock_irqsave(&elm_lock, flags);
|
|
err = snprintf(buf, leng,
|
|
"[EMI ELM] ddr data rate: %d, valid: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n%s: 0x%08x\n"
|
|
"%s: 0x%08x\n",
|
|
dram_data_rate, elm_valid,
|
|
"EMI_CGMA_ST0", emi_cgma_st0,
|
|
"EMI_CGMA_ST1", emi_cgma_st1,
|
|
"EMI_CGMA_ST2", emi_cgma_st2,
|
|
"EMI_EBMINT_ST", emi_ebmint_st,
|
|
"EMI_LTCT0", emi_ltct0,
|
|
"EMI_LTCT1", emi_ltct1,
|
|
"EMI_LTCT2", emi_ltct2,
|
|
"EMI_LTCT3", emi_ltct3,
|
|
"EMI_LTST0", emi_ltst0,
|
|
"EMI_LTST1", emi_ltst1,
|
|
"EMI_LTST2", emi_ltst2,
|
|
"EMI_BMEN", emi_bmen,
|
|
"EMI_BMEN2", emi_bmen2,
|
|
"EMI_MSEL", emi_msel,
|
|
"EMI_MSEL2", emi_msel2,
|
|
"EMI_BMRW0", emi_bmrw0,
|
|
"EMI_BCNT", emi_bcnt,
|
|
"EMI_WSCT", emi_wsct,
|
|
"EMI_WSCT2", emi_wsct2,
|
|
"EMI_WSCT3", emi_wsct3,
|
|
"EMI_WSCT4", emi_wsct4,
|
|
"EMI_TTYPE1", emi_ttype[0],
|
|
"EMI_TTYPE2", emi_ttype[1],
|
|
"EMI_TTYPE3", emi_ttype[2],
|
|
"EMI_TTYPE4", emi_ttype[3],
|
|
"EMI_TTYPE5", emi_ttype[4],
|
|
"EMI_TTYPE6", emi_ttype[5],
|
|
"EMI_TTYPE7", emi_ttype[6],
|
|
"EMI_TTYPE8", emi_ttype[7],
|
|
"EMI_TTYPE9", emi_ttype[8],
|
|
"EMI_TTYPE10", emi_ttype[9],
|
|
"EMI_TTYPE11", emi_ttype[10],
|
|
"EMI_TTYPE12", emi_ttype[11],
|
|
"EMI_TTYPE13", emi_ttype[12],
|
|
"EMI_TTYPE14", emi_ttype[13],
|
|
"EMI_TTYPE15", emi_ttype[14],
|
|
"EMI_TTYPE16", emi_ttype[15]
|
|
);
|
|
spin_unlock_irqrestore(&elm_lock, flags);
|
|
if (err < 0)
|
|
buf = NULL;
|
|
}
|
|
|
|
/* elm_irq handler */
|
|
static irqreturn_t elm_isr(int irq, void *dev_id)
|
|
{
|
|
unsigned long flags;
|
|
int emi_dcm_status;
|
|
|
|
pr_info("[EMI ELM] violation\n");
|
|
lt_reg_save();
|
|
|
|
/* Reset counter */
|
|
spin_lock_irqsave(&elm_lock, flags);
|
|
emi_dcm_status = BM_GetEmiDcm();
|
|
BM_SetEmiDcm(0x40);
|
|
mt_reg_sync_writel(readl(IOMEM(EMI_BMEN)) & 0xfffffffc, EMI_BMEN);
|
|
mt_reg_sync_writel(readl(IOMEM(EMI_LTCT0_2ND)) | 0x80000000, EMI_LTCT0_2ND);
|
|
mt_reg_sync_writel(readl(IOMEM(EMI_BMEN)) | 0x1, EMI_BMEN);
|
|
BM_SetEmiDcm(emi_dcm_status);
|
|
spin_unlock_irqrestore(&elm_lock, flags);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static struct platform_driver elm_ctrl = {
|
|
.driver = {
|
|
.name = "elm_ctrl",
|
|
.bus = &platform_bus_type,
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.id_table = NULL,
|
|
};
|
|
|
|
int elm_init(unsigned int elm_irq)
|
|
{
|
|
int ret = 0;
|
|
|
|
CEN_EMI_BASE = mt_cen_emi_base_get();
|
|
|
|
if (elm_irq != 0) {
|
|
ret = request_irq(elm_irq, (irq_handler_t)elm_isr, IRQF_TRIGGER_NONE, "elm", &elm_ctrl);
|
|
if (ret != 0) {
|
|
pr_info("Fail to request emi_bw_irqnr interrupt. Error = %d.\n", ret);
|
|
return ret;
|
|
}
|
|
} else
|
|
pr_info("[EMI] fail to register elm_irq\n");
|
|
|
|
elm_valid = 0;
|
|
lt_init();
|
|
|
|
return ret;
|
|
}
|
|
|
|
void suspend_elm(void)
|
|
{
|
|
unsigned long flags;
|
|
int emi_dcm_status;
|
|
|
|
spin_lock_irqsave(&elm_lock, flags);
|
|
emi_dcm_status = BM_GetEmiDcm();
|
|
BM_SetEmiDcm(0x40);
|
|
|
|
/* Clear BM */
|
|
mt_reg_sync_writel(readl(IOMEM(EMI_BMEN)) & 0xfffffffc, EMI_BMEN);
|
|
|
|
/* Disable CGM */
|
|
writel(0x0, IOMEM(EMI_CGMA));
|
|
|
|
/* Disable ELM */
|
|
writel(readl(IOMEM(EMI_LTCT0_2ND)) & ~0x1, IOMEM(EMI_LTCT0_2ND));
|
|
mt_reg_sync_writel(readl(IOMEM(EMI_LTCT0_2ND)) & ~0x8, EMI_LTCT0_2ND);
|
|
|
|
BM_SetEmiDcm(emi_dcm_status);
|
|
spin_unlock_irqrestore(&elm_lock, flags);
|
|
}
|
|
|
|
void resume_elm(void)
|
|
{
|
|
if (is_elm_enabled())
|
|
lt_init();
|
|
}
|
|
|