c05564c4d8
Android 13
1574 lines
39 KiB
C
Executable file
1574 lines
39 KiB
C
Executable file
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2020 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/fs.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/module.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/sched/clock.h>
|
|
|
|
#include <soc/mediatek/smi.h>
|
|
#include <mtk_smi.h>
|
|
#include <aee.h>
|
|
|
|
#if IS_ENABLED(CONFIG_COMPAT)
|
|
#include <linux/compat.h>
|
|
#endif
|
|
|
|
#include <smi_conf.h>
|
|
#include <smi_public.h>
|
|
#include <smi_pmqos.h>
|
|
#include <mmdvfs_pmqos.h>
|
|
#if IS_ENABLED(CONFIG_MEDIATEK_EMI)
|
|
#include <memory/mediatek/emi.h>
|
|
#elif IS_ENABLED(CONFIG_MTK_EMI)
|
|
//#include <plat_debug_api.h>
|
|
#elif IS_ENABLED(CONFIG_MTK_EMI_BWL)
|
|
#include <emi_mbw.h>
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_MTK_IOMMU_V2)
|
|
//#include <mach/mt_iommu.h>
|
|
#elif IS_ENABLED(CONFIG_MTK_M4U)
|
|
//#include <m4u.h>
|
|
#endif
|
|
#ifdef MMDVFS_HOOK
|
|
#include <mmdvfs_mgr.h>
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_TINYSYS_SSPM_SUPPORT)
|
|
#include <sspm_define.h>
|
|
#if IS_ENABLED(SMI_SSPM)
|
|
#include <sspm_reservedmem_define.h>
|
|
#include <sspm_ipi_id.h>
|
|
static bool smi_sspm_ipi_register;
|
|
#else
|
|
#include <sspm_ipi.h>
|
|
//#include <sspm_reservedmem_define.h>
|
|
#if IS_ENABLED(CONFIG_MACH_MT6761)
|
|
#include <clk-mt6761-pg.h>
|
|
#include <sspm_reservedmem_define_mt6761.h>
|
|
#elif IS_ENABLED(CONFIG_MACH_MT6779)
|
|
#include <sspm_reservedmem_define_mt6779.h>
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#define DEV_NAME "MTK_SMI"
|
|
#undef pr_fmt
|
|
#define pr_fmt(fmt) "[" DEV_NAME "]" fmt
|
|
|
|
#define SMIDBG(string, args...) pr_debug(string, ##args)
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_CMDQ_MBOX_EXT)
|
|
#include <cmdq-util.h>
|
|
#define SMIWRN(cmdq, string, args...) \
|
|
do { \
|
|
if (cmdq != 0) \
|
|
cmdq_util_msg(string, ##args); \
|
|
else \
|
|
pr_info(string, ##args); \
|
|
} while (0)
|
|
#elif IS_ENABLED(CONFIG_MTK_CMDQ)
|
|
#include <cmdq_helper_ext.h>
|
|
#define SMIWRN(cmdq, string, args...) \
|
|
do { \
|
|
if (cmdq != 0) \
|
|
cmdq_core_save_first_dump(string, ##args); \
|
|
pr_info(string, ##args); \
|
|
} while (0)
|
|
#else
|
|
#define SMIWRN(cmdq, string, args...) pr_info(string, ##args)
|
|
#endif
|
|
|
|
#define SMIERR(string, args...) \
|
|
do { \
|
|
pr_notice(string, ##args); \
|
|
aee_kernel_warning(DEV_NAME, string, ##args); \
|
|
} while (0)
|
|
|
|
#ifndef ATOMR_CLK
|
|
#define ATOMR_CLK(i) atomic_read(&(smi_dev[(i)]->clk_cnts))
|
|
#endif
|
|
|
|
struct smi_driver_t {
|
|
spinlock_t lock;
|
|
enum MTK_SMI_BWC_SCEN scen;
|
|
s32 table[SMI_BWC_SCEN_CNT];
|
|
};
|
|
|
|
struct smi_record_t {
|
|
/* clk from api */
|
|
char user[NAME_MAX];
|
|
u64 clk_sec;
|
|
u32 clk_nsec;
|
|
/* mtcmos cb from ccf */
|
|
atomic_t on;
|
|
u64 sec;
|
|
u32 nsec;
|
|
};
|
|
|
|
struct smi_dram_t {
|
|
u8 dump;
|
|
phys_addr_t size;
|
|
void __iomem *virt;
|
|
struct dentry *node;
|
|
s32 ackdata;
|
|
};
|
|
|
|
static struct smi_driver_t smi_drv;
|
|
static struct smi_record_t smi_record[SMI_LARB_NUM][2];
|
|
static struct smi_dram_t smi_dram;
|
|
|
|
static struct mtk_smi_dev *smi_dev[SMI_DEV_NUM];
|
|
|
|
static bool smi_reg_first, smi_bwc_conf_dis;
|
|
static u32 smi_subsys_on, smi_mm_first;
|
|
|
|
bool smi_mm_first_get(void)
|
|
{
|
|
return smi_mm_first ? true : false;
|
|
}
|
|
EXPORT_SYMBOL_GPL(smi_mm_first_get);
|
|
|
|
static void smi_clk_record(const u32 id, const bool en, const char *user)
|
|
{
|
|
struct smi_record_t *record;
|
|
u64 sec;
|
|
u32 nsec;
|
|
|
|
if (id >= SMI_LARB_NUM)
|
|
return;
|
|
record = &smi_record[id][en ? 1 : 0];
|
|
sec = sched_clock();
|
|
nsec = do_div(sec, 1000000000) / 1000;
|
|
if (user) {
|
|
record->clk_sec = sec;
|
|
record->clk_nsec = nsec;
|
|
strncpy(record->user, user, NAME_MAX);
|
|
record->user[sizeof(record->user) - 1] = '\0';
|
|
} else {
|
|
record->sec = sec;
|
|
record->nsec = nsec;
|
|
atomic_set(&(record->on), en ? 1 : 0);
|
|
atomic_set(&(smi_record[id][en ? 0 : 1].on), en ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
static inline s32 smi_unit_prepare_enable(const u32 id)
|
|
{
|
|
s32 ret = 0;
|
|
|
|
ret = clk_prepare_enable(smi_dev[id]->clks[0]);
|
|
if (ret) {
|
|
SMIERR("SMI%u MTCMOS enable failed: %d\n", id, ret);
|
|
return ret;
|
|
}
|
|
return mtk_smi_clk_enable(smi_dev[id]);
|
|
}
|
|
|
|
s32 smi_bus_prepare_enable(const u32 id, const char *user)
|
|
{
|
|
s32 ret;
|
|
|
|
if (id >= SMI_DEV_NUM) {
|
|
SMIDBG("Invalid id:%u, SMI_DEV_NUM=%u, user=%s\n",
|
|
id, SMI_DEV_NUM, user);
|
|
return -EINVAL;
|
|
} else if (id < SMI_LARB_NUM) {
|
|
smi_clk_record(id, true, user);
|
|
}
|
|
|
|
if (!smi_dev[id]) {
|
|
SMIERR("SMI %s: %d is not ready.\n", __func__, id);
|
|
return -EPROBE_DEFER;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_MACH_MT6885) || IS_ENABLED(CONFIG_MACH_MT6893)
|
|
if (id == 6 || id == 10 || id == 12) {
|
|
SMIDBG("Invalid id:%u user:%s\n", id, user);
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (id) {
|
|
case 0:
|
|
case 1:
|
|
case 5:
|
|
case 7:
|
|
case 14:
|
|
case 17:
|
|
ret = smi_unit_prepare_enable(21); // disp
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(24); // disp-subcom
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(25); // disp-subcom1
|
|
if (ret)
|
|
return ret;
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 8:
|
|
case 9:
|
|
case 13:
|
|
case 16:
|
|
case 18:
|
|
ret = smi_unit_prepare_enable(22); // mdp
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(26); // mdp-subcom
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(27); // mdp-subcom1
|
|
if (ret)
|
|
return ret;
|
|
break;
|
|
case 11:
|
|
case 19:
|
|
case 20:
|
|
ret = smi_unit_prepare_enable(21); // disp
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(24); // disp-subcom
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(25); // disp-subcom1
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(22); // mdp
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(26); // mdp-subcom
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(27); // mdp-subcom1
|
|
if (ret)
|
|
return ret;
|
|
break;
|
|
}
|
|
|
|
switch (id) {
|
|
case 2:
|
|
case 3:
|
|
case 7:
|
|
case 8:
|
|
ret = smi_unit_prepare_enable(23); // sysram
|
|
if (ret)
|
|
return ret;
|
|
break;
|
|
case 13:
|
|
ret = smi_unit_prepare_enable(23); // sysram
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(29); // cam-subcom
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(31); // cam-subcom2
|
|
if (ret)
|
|
return ret;
|
|
break;
|
|
case 14:
|
|
ret = smi_unit_prepare_enable(23); // sysram
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(30); // cam-subcom1
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(31); // cam-subcom2
|
|
if (ret)
|
|
return ret;
|
|
break;
|
|
case 16:
|
|
case 17:
|
|
case 18:
|
|
ret = smi_unit_prepare_enable(23); // sysram
|
|
if (ret)
|
|
return ret;
|
|
ret = smi_unit_prepare_enable(31); // cam-subcom2
|
|
if (ret)
|
|
return ret;
|
|
break;
|
|
case 19:
|
|
case 20:
|
|
ret = smi_unit_prepare_enable(28); // ipe-subcom
|
|
if (ret)
|
|
return ret;
|
|
break;
|
|
}
|
|
#else // !CONFIG_MACH_MT6885
|
|
ret = smi_unit_prepare_enable(SMI_LARB_NUM);
|
|
if (ret || id == SMI_LARB_NUM)
|
|
return ret;
|
|
|
|
#endif
|
|
|
|
#if IS_ENABLED(SMI_5G)
|
|
if (id == 4) {
|
|
ret = smi_unit_prepare_enable(5);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
#endif
|
|
return smi_unit_prepare_enable(id);
|
|
}
|
|
EXPORT_SYMBOL_GPL(smi_bus_prepare_enable);
|
|
|
|
static inline void smi_unit_disable_unprepare(const u32 id)
|
|
{
|
|
mtk_smi_clk_disable(smi_dev[id]);
|
|
clk_disable_unprepare(smi_dev[id]->clks[0]);
|
|
}
|
|
|
|
s32 smi_bus_disable_unprepare(const u32 id, const char *user)
|
|
{
|
|
if (!smi_dev[id]) {
|
|
SMIERR("SMI %s: %d is not ready.\n", __func__, id);
|
|
return -EPROBE_DEFER;
|
|
}
|
|
|
|
if (id >= SMI_DEV_NUM) {
|
|
SMIDBG("Invalid id:%u, SMI_DEV_NUM=%u, user=%s\n",
|
|
id, SMI_DEV_NUM, user);
|
|
return -EINVAL;
|
|
} else if (id == SMI_LARB_NUM) {
|
|
smi_unit_disable_unprepare(id);
|
|
return 0;
|
|
} else if (id < SMI_LARB_NUM) {
|
|
smi_clk_record(id, false, user);
|
|
}
|
|
|
|
if (ATOMR_CLK(id) == 1 && readl(smi_dev[id]->base + SMI_LARB_STAT))
|
|
SMIWRN(1, "LARB%u OFF by %s but busy\n", id, user);
|
|
#if IS_ENABLED(CONFIG_MACH_MT6885) || IS_ENABLED(CONFIG_MACH_MT6893)
|
|
if (id == 6 || id == 10 || id == 12) {
|
|
SMIDBG("Invalid id:%u user:%s\n", id, user);
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
smi_unit_disable_unprepare(id);
|
|
#if IS_ENABLED(SMI_5G)
|
|
if (id == 4)
|
|
smi_unit_disable_unprepare(5);
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MACH_MT6885) || IS_ENABLED(CONFIG_MACH_MT6893)
|
|
switch (id) {
|
|
case 2:
|
|
case 3:
|
|
case 7:
|
|
case 8:
|
|
smi_unit_disable_unprepare(23); // sysram
|
|
break;
|
|
case 13:
|
|
smi_unit_disable_unprepare(31); // cam-subcom2
|
|
smi_unit_disable_unprepare(29); // cam-subcom
|
|
smi_unit_disable_unprepare(23); // sysram
|
|
break;
|
|
case 14:
|
|
smi_unit_disable_unprepare(31); // cam-subcom2
|
|
smi_unit_disable_unprepare(30); // cam-subcom1
|
|
smi_unit_disable_unprepare(23); // sysram
|
|
break;
|
|
case 16:
|
|
case 17:
|
|
case 18:
|
|
smi_unit_disable_unprepare(31); // cam-subcom2
|
|
smi_unit_disable_unprepare(23); // sysram
|
|
break;
|
|
case 19:
|
|
case 20:
|
|
smi_unit_disable_unprepare(28); // ipe-subcom
|
|
break;
|
|
}
|
|
|
|
switch (id) {
|
|
case 0:
|
|
case 1:
|
|
case 5:
|
|
case 7:
|
|
case 14:
|
|
case 17:
|
|
smi_unit_disable_unprepare(25); // disp-subcom1
|
|
smi_unit_disable_unprepare(24); // disp-subcom
|
|
smi_unit_disable_unprepare(21); // disp
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 8:
|
|
case 9:
|
|
case 13:
|
|
case 16:
|
|
case 18:
|
|
smi_unit_disable_unprepare(27); // mdp-subcom1
|
|
smi_unit_disable_unprepare(26); // mdp-subcom
|
|
smi_unit_disable_unprepare(22); // mdp
|
|
break;
|
|
case 11:
|
|
case 19:
|
|
case 20:
|
|
smi_unit_disable_unprepare(27); // mdp-subcom1
|
|
smi_unit_disable_unprepare(26); // mdp-subcom
|
|
smi_unit_disable_unprepare(22); // mdp
|
|
|
|
smi_unit_disable_unprepare(25); // disp-subcom1
|
|
smi_unit_disable_unprepare(24); // disp-subcom
|
|
smi_unit_disable_unprepare(21); // disp
|
|
break;
|
|
}
|
|
#else // !CONFIG_MACH_MT6885
|
|
smi_unit_disable_unprepare(SMI_LARB_NUM);
|
|
#endif
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(smi_bus_disable_unprepare);
|
|
|
|
void
|
|
smi_bwl_update(const u32 id, const u32 bwl, const bool soft, const char *user)
|
|
{
|
|
u32 val, comm = 0;
|
|
|
|
if ((id & 0xffff) >= SMI_COMM_MASTER_NUM) {
|
|
SMIDBG("Invalid id:%#x SMI_COMM_MASTER_NUM:%u\n",
|
|
id, SMI_COMM_MASTER_NUM);
|
|
return;
|
|
}
|
|
#if IS_ENABLED(CONFIG_MACH_MT6885) || IS_ENABLED(CONFIG_MACH_MT6893)
|
|
comm = id >> 16;
|
|
#endif
|
|
val = (soft ? 0x1000 : 0x3000) | SMI_PMQOS_BWL_MASK(bwl);
|
|
smi_scen_pair[SMI_LARB_NUM + comm][SMI_ESL_INIT][id & 0xffff].val = val;
|
|
|
|
if (ATOMR_CLK(SMI_LARB_NUM + comm)) {
|
|
smi_bus_prepare_enable(SMI_LARB_NUM + comm, user);
|
|
writel(val, smi_dev[SMI_LARB_NUM + comm]->base + smi_scen_pair[
|
|
SMI_LARB_NUM + comm][SMI_ESL_INIT][id & 0xffff].off);
|
|
smi_bus_disable_unprepare(SMI_LARB_NUM + comm, user);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(smi_bwl_update);
|
|
|
|
void smi_ostd_update(const struct plist_head *head, const char *user)
|
|
{
|
|
struct mm_qos_request *req;
|
|
u32 larb, port, ostd;
|
|
|
|
if (plist_head_empty(head))
|
|
return;
|
|
#if defined (CONFIG_MACH_MT6833)
|
|
// TODO Migration
|
|
if (smi_dev[larb] == NULL){
|
|
pr_notice("[SMI 6833] ERROR smi_ostd_update larb not ready \n");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
plist_for_each_entry(req, head, owner_node) {
|
|
larb = SMI_PMQOS_LARB_DEC(req->master_id);
|
|
if (!req->updated || larb >= SMI_LARB_NUM)
|
|
continue;
|
|
|
|
port = SMI_PMQOS_PORT_MASK(req->master_id);
|
|
if (!req->ostd)
|
|
ostd = 0x1;
|
|
else if (req->ostd > SMI_OSTD_MAX)
|
|
ostd = SMI_OSTD_MAX;
|
|
else
|
|
ostd = req->ostd;
|
|
smi_scen_pair[larb][SMI_ESL_INIT][port].val = ostd;
|
|
|
|
if (ATOMR_CLK(larb)) {
|
|
smi_bus_prepare_enable(larb, user);
|
|
writel(ostd, smi_dev[larb]->base +
|
|
smi_scen_pair[larb][SMI_ESL_INIT][port].off);
|
|
smi_bus_disable_unprepare(larb, user);
|
|
}
|
|
req->updated = false;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(smi_ostd_update);
|
|
|
|
s32 smi_sysram_enable(const u32 master_id, const bool enable, const char *user)
|
|
{
|
|
#if IS_ENABLED(CONFIG_MACH_MT6885) || IS_ENABLED(CONFIG_MACH_MT6893)
|
|
u32 larb = MTK_IOMMU_TO_LARB(master_id);
|
|
u32 port = MTK_IOMMU_TO_PORT(master_id);
|
|
u32 ostd[2], val;
|
|
|
|
smi_bus_prepare_enable(larb, user);
|
|
ostd[0] = readl(smi_dev[larb]->base + SMI_LARB_OSTD_MON_PORT(port));
|
|
ostd[1] = readl(smi_dev[larb]->base + INT_SMI_LARB_OSTD_MON_PORT(port));
|
|
if (ostd[0] || ostd[1]) {
|
|
aee_kernel_exception(user,
|
|
"%s set larb%u port%u sysram %d failed ostd:%u %u\n",
|
|
user, larb, port, enable, ostd[0], ostd[1]);
|
|
smi_bus_disable_unprepare(larb, user);
|
|
return (ostd[1] << 16) | ostd[0];
|
|
}
|
|
|
|
val = readl(smi_dev[larb]->base + SMI_LARB_NON_SEC_CON(port));
|
|
writel(val | ((enable ? 0xf : 0) << 16),
|
|
smi_dev[larb]->base + SMI_LARB_NON_SEC_CON(port));
|
|
SMIDBG("%s set larb%u port%u sysram %d succeeded\n",
|
|
user, larb, port, enable);
|
|
smi_bus_disable_unprepare(larb, user);
|
|
#endif
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(smi_sysram_enable);
|
|
|
|
static inline void
|
|
smi_debug_print(const bool gce, const u32 num, const u32 *pos, const u32 *val)
|
|
{
|
|
char buf[LINK_MAX + 1];
|
|
s32 len, i, j, ret;
|
|
|
|
for (i = 0; i < num; i += j) {
|
|
len = 0;
|
|
for (j = 0; i + j < num; j++) {
|
|
if (!val)
|
|
ret = snprintf(buf + len, LINK_MAX - len,
|
|
" %#x,", pos[i + j]);
|
|
else if (val[i + j])
|
|
ret = snprintf(buf + len, LINK_MAX - len,
|
|
" %#x=%#x,", pos[i + j], val[i + j]);
|
|
else
|
|
continue;
|
|
|
|
if (ret < 0 || len + ret >= LINK_MAX) {
|
|
ret = snprintf(buf + len, LINK_MAX - len,
|
|
"%c", '\0');
|
|
if (ret < 0)
|
|
SMIDBG("snprintf return error:%d\n",
|
|
ret);
|
|
break;
|
|
}
|
|
len += ret;
|
|
}
|
|
SMIWRN(gce, "%s\n", buf);
|
|
}
|
|
}
|
|
|
|
static s32 smi_debug_dumper(const bool gce, const bool off, const u32 id)
|
|
{
|
|
char *name;
|
|
void __iomem *base;
|
|
u32 nr_debugs, *debugs, temp[MAX_INPUT];
|
|
s32 i, j;
|
|
#if IS_ENABLED(CONFIG_MACH_MT6761)
|
|
unsigned long flags = 0;
|
|
#endif
|
|
|
|
if (id > SMI_DEV_NUM) {
|
|
SMIDBG("Invalid id:%u, SMI_DEV_NUM=%u\n", id, SMI_DEV_NUM);
|
|
return -EINVAL;
|
|
}
|
|
j = (id == SMI_DEV_NUM ? SMI_LARB_NUM : id);
|
|
name = (id == SMI_DEV_NUM ? "MMSYS" :
|
|
(id < SMI_LARB_NUM ? "LARB" : "COMM"));
|
|
base = (id == SMI_DEV_NUM ? smi_mmsys_base : smi_dev[id]->base);
|
|
nr_debugs = (id == SMI_DEV_NUM ? SMI_MMSYS_DEBUG_NUM :
|
|
(id < SMI_LARB_NUM ? SMI_LARB_DEBUG_NUM : SMI_COMM_DEBUG_NUM));
|
|
debugs = (id == SMI_DEV_NUM ?
|
|
smi_mmsys_debug_offset : (id < SMI_LARB_NUM ?
|
|
smi_larb_debug_offset : smi_comm_debug_offset));
|
|
if (!base || !nr_debugs || !debugs) {
|
|
SMIDBG("Invalid base, nr_debugs, debugs of %s%u\n", name, id);
|
|
return -ENXIO;
|
|
}
|
|
|
|
if (off) {
|
|
SMIWRN(gce, "======== %s offset ========\n", name);
|
|
smi_debug_print(gce, nr_debugs, debugs, NULL);
|
|
return 0;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_MACH_MT6761)
|
|
mtk_mtcmos_lock(flags);
|
|
#endif
|
|
for (i = 0; i < nr_debugs && ATOMR_CLK(j) > 0; i++)
|
|
temp[i] = readl(base + debugs[i]);
|
|
#if IS_ENABLED(CONFIG_MACH_MT6761)
|
|
mtk_mtcmos_unlock(flags);
|
|
#endif
|
|
if (i < nr_debugs) {
|
|
SMIWRN(gce, "======== %s%u OFF ========\n", name, id);
|
|
return 0;
|
|
}
|
|
|
|
SMIWRN(gce, "======== %s%u non-zero value, clk:%d ========\n",
|
|
name, id, ATOMR_CLK(j));
|
|
smi_debug_print(gce, nr_debugs, debugs, temp);
|
|
return 0;
|
|
}
|
|
|
|
static void smi_debug_dump_status(const bool gce)
|
|
{
|
|
s32 on, i;
|
|
|
|
for (i = 0; i <= SMI_DEV_NUM; i++)
|
|
smi_debug_dumper(gce, false, i);
|
|
|
|
SMIWRN(gce, "SCEN=%s(%d), SMI_SCEN=%d\n",
|
|
smi_bwc_scen_name_get(smi_drv.scen),
|
|
smi_drv.scen, smi_scen_map[smi_drv.scen]);
|
|
|
|
for (i = 0; i < SMI_LARB_NUM; i++) {
|
|
on = atomic_read(&(smi_record[i][0].on));
|
|
SMIWRN(gce,
|
|
"LARB%u:[%cON][%5llu.%6u][%16s]/[%cOFF][%5llu.%6u][%16s]\n",
|
|
i, on ? '*' : ' ', smi_record[i][1].sec,
|
|
smi_record[i][1].nsec, smi_record[i][1].user,
|
|
on ? ' ' : '*', smi_record[i][0].sec,
|
|
smi_record[i][0].nsec, smi_record[i][0].user);
|
|
}
|
|
}
|
|
|
|
s32 smi_debug_bus_hang_detect(const bool gce, const char *user)
|
|
{
|
|
u32 time = 5, busy[SMI_DEV_NUM] = {0};
|
|
s32 i, j, ret = 0;
|
|
#if IS_ENABLED(CONFIG_MACH_MT6761)
|
|
unsigned long flags = 0;
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MEDIATEK_EMI)
|
|
mtk_emidbg_dump();
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_IOMMU_V2)
|
|
//mtk_dump_reg_for_hang_issue();
|
|
#elif IS_ENABLED(CONFIG_MTK_M4U)
|
|
//m4u_dump_reg_for_smi_hang_issue();
|
|
#endif
|
|
|
|
if (!smi_dev[0]) {
|
|
SMIERR("SMI %s is not ready.\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < time; i++) {
|
|
for (j = 0; j < SMI_LARB_NUM; j++) {
|
|
#if IS_ENABLED(CONFIG_MACH_MT6761)
|
|
mtk_mtcmos_lock(flags);
|
|
#endif
|
|
busy[j] += ((ATOMR_CLK(j) > 0 &&
|
|
readl(smi_dev[j]->base + SMI_LARB_STAT)) ? 1 : 0);
|
|
#if IS_ENABLED(CONFIG_MACH_MT6761)
|
|
mtk_mtcmos_unlock(flags);
|
|
#endif
|
|
}
|
|
/* COMM */
|
|
for (j = SMI_LARB_NUM; j < SMI_DEV_NUM; j++)
|
|
busy[j] += ((ATOMR_CLK(j) > 0 &&
|
|
!(readl(smi_dev[j]->base + SMI_DEBUG_MISC) &
|
|
0x1)) ? 1 : 0);
|
|
}
|
|
|
|
for (i = 0; i < SMI_LARB_NUM && !ret; i++)
|
|
ret = (busy[i] == time ? i : ret);
|
|
if (!ret || busy[SMI_LARB_NUM] < time) {
|
|
SMIWRN(gce, "SMI MM bus NOT hang, check master %s\n", user);
|
|
smi_debug_dump_status(gce);
|
|
return 0;
|
|
}
|
|
|
|
SMIWRN(gce, "SMI MM bus may hang by %s/M4U/EMI/DVFS\n", user);
|
|
for (i = 0; i <= SMI_DEV_NUM; i++)
|
|
if (!i || i == SMI_LARB_NUM || i == SMI_DEV_NUM)
|
|
smi_debug_dumper(gce, true, i);
|
|
|
|
for (i = 1; i < time; i++)
|
|
for (j = 0; j <= SMI_DEV_NUM; j++)
|
|
smi_debug_dumper(gce, false, j);
|
|
smi_debug_dump_status(gce);
|
|
|
|
for (i = 0; i < SMI_DEV_NUM; i++)
|
|
SMIWRN(gce, "%s%u=%u/%u busy with clk:%d\n",
|
|
i < SMI_LARB_NUM ? "LARB" : "COMMON",
|
|
i, busy[i], time, ATOMR_CLK(i));
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(smi_debug_bus_hang_detect);
|
|
|
|
static inline void smi_larb_port_set(const struct mtk_smi_dev *smi)
|
|
{
|
|
s32 i;
|
|
|
|
if (!smi || !smi->dev || smi->id >= SMI_LARB_NUM)
|
|
return;
|
|
|
|
for (i = smi_larb_cmd_gp_en_port[smi->id][0];
|
|
i < smi_larb_cmd_gp_en_port[smi->id][1]; i++)
|
|
writel(readl(smi->base + SMI_LARB_NON_SEC_CON(i)) | 0x2,
|
|
smi->base + SMI_LARB_NON_SEC_CON(i));
|
|
|
|
for (i = smi_larb_bw_thrt_en_port[smi->id][0];
|
|
i < smi_larb_bw_thrt_en_port[smi->id][1]; i++)
|
|
writel(readl(smi->base + SMI_LARB_NON_SEC_CON(i)) | 0x8,
|
|
smi->base + SMI_LARB_NON_SEC_CON(i));
|
|
#if IS_ENABLED(CONFIG_MACH_MT6853)
|
|
if (readl(smi->base + SMI_LARB_NON_SEC_CON(i)) & 0x4)
|
|
pr_info("[SMI LOG]smi_larb%d, port%d[2]:%#x\n",
|
|
smi->id, i,
|
|
readl(smi->base + SMI_LARB_NON_SEC_CON(i)));
|
|
#endif
|
|
|
|
|
|
#if IS_ENABLED(CONFIG_MACH_MT6765) || IS_ENABLED(CONFIG_MACH_MT6768) || \
|
|
IS_ENABLED(CONFIG_MACH_MT6771)
|
|
if (!smi->id) /* mm-infra gals DCM */
|
|
writel(0x780000, smi_mmsys_base + MMSYS_HW_DCM_1ST_DIS_SET0);
|
|
#endif
|
|
}
|
|
|
|
s32 smi_larb_port_check(void)
|
|
{
|
|
#if IS_ENABLED(CONFIG_MACH_MT6853)
|
|
s32 i;
|
|
|
|
mtk_smi_clk_enable(smi_dev[2]);
|
|
|
|
for (i = smi_larb_bw_thrt_en_port[2][0];
|
|
i < smi_larb_bw_thrt_en_port[2][1]; i++)
|
|
if (readl(smi_dev[2]->base + SMI_LARB_NON_SEC_CON(i)) & 0x4) {
|
|
pr_info("[SMI LOG]cmdq smi_larb2 port%d:%#x\n",
|
|
i, readl(smi_dev[2]->base
|
|
+ SMI_LARB_NON_SEC_CON(i)));
|
|
writel(readl(smi_dev[2]->base + SMI_LARB_NON_SEC_CON(i))
|
|
& 0xFFFFFFFB,
|
|
smi_dev[2]->base + SMI_LARB_NON_SEC_CON(i));
|
|
pr_info("[SMI LOG]new cmdq smi_larb2 port%d:%#x\n",
|
|
i, readl(smi_dev[2]->base
|
|
+ SMI_LARB_NON_SEC_CON(i)));
|
|
}
|
|
|
|
mtk_smi_clk_disable(smi_dev[2]);
|
|
#endif
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(smi_larb_port_check);
|
|
|
|
|
|
static s32 smi_bwc_conf(const struct MTK_SMI_BWC_CONF *conf)
|
|
{
|
|
s32 same = 0, smi_scen, i;
|
|
|
|
if (smi_bwc_conf_dis) {
|
|
SMIDBG("SMI BWC configuration disable: %u\n", smi_bwc_conf_dis);
|
|
return 0;
|
|
} else if (!conf) {
|
|
SMIDBG("MTK_SMI_BWC_CONF no such device or address\n");
|
|
return -ENXIO;
|
|
} else if (conf->scen >= SMI_BWC_SCEN_CNT) {
|
|
SMIDBG("Invalid conf scenario:%u, SMI_BWC_SCEN_CNT=%u\n",
|
|
conf->scen, SMI_BWC_SCEN_CNT);
|
|
return -EINVAL;
|
|
}
|
|
|
|
spin_lock(&(smi_drv.lock));
|
|
if (!conf->b_on) {
|
|
if (smi_drv.table[conf->scen] <= 0)
|
|
SMIWRN(0, "ioctl=%s:OFF not in pairs\n",
|
|
smi_bwc_scen_name_get(conf->scen));
|
|
else
|
|
smi_drv.table[conf->scen] -= 1;
|
|
} else
|
|
smi_drv.table[conf->scen] += 1;
|
|
|
|
for (i = SMI_BWC_SCEN_CNT - 1; i >= 0; i--)
|
|
if (smi_drv.table[i])
|
|
break;
|
|
i = (i < 0 ? 0 : i);
|
|
if (smi_scen_map[i] == smi_scen_map[smi_drv.scen])
|
|
same += 1;
|
|
smi_drv.scen = (i > 0 ? i : 0);
|
|
spin_unlock(&(smi_drv.lock));
|
|
|
|
#ifdef MMDVFS_HOOK
|
|
{
|
|
unsigned int concurrency = 0;
|
|
|
|
if (conf->b_on)
|
|
mmdvfs_notify_scenario_enter(conf->scen);
|
|
else
|
|
mmdvfs_notify_scenario_exit(conf->scen);
|
|
|
|
for (i = 0; i < SMI_BWC_SCEN_CNT; i++)
|
|
concurrency |= (smi_drv.table[i] ? 1 : 0) << i;
|
|
mmdvfs_notify_scenario_concurrency(concurrency);
|
|
}
|
|
#endif
|
|
|
|
smi_scen = smi_scen_map[smi_drv.scen];
|
|
if (same) {
|
|
SMIDBG("ioctl=%s:%s, curr=%s(%d), SMI_SCEN=%d [same as prev]\n",
|
|
smi_bwc_scen_name_get(conf->scen),
|
|
conf->b_on ? "ON" : "OFF",
|
|
smi_bwc_scen_name_get(smi_drv.scen),
|
|
smi_drv.scen, smi_scen);
|
|
return 0;
|
|
}
|
|
for (i = 0; i < SMI_DEV_NUM; i++) {
|
|
#if IS_ENABLED(CONFIG_MACH_MT6768)
|
|
smi_bus_prepare_enable(i, DEV_NAME);
|
|
mtk_smi_conf_set(smi_dev[i], smi_scen);
|
|
smi_bus_disable_unprepare(i, DEV_NAME);
|
|
#else
|
|
mtk_smi_conf_set(smi_dev[i], smi_scen);
|
|
#endif
|
|
}
|
|
|
|
SMIDBG("ioctl=%s:%s, curr=%s(%d), SMI_SCEN=%d\n",
|
|
smi_bwc_scen_name_get(conf->scen), conf->b_on ? "ON" : "OFF",
|
|
smi_bwc_scen_name_get(smi_drv.scen), smi_drv.scen, smi_scen);
|
|
return 0;
|
|
}
|
|
|
|
static s32 smi_open(struct inode *inode, struct file *file)
|
|
{
|
|
file->private_data = kcalloc(SMI_BWC_SCEN_CNT, sizeof(u32), GFP_ATOMIC);
|
|
if (!file->private_data) {
|
|
SMIERR("Allocate file private data failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static s32 smi_release(struct inode *inode, struct file *file)
|
|
{
|
|
kfree(file->private_data);
|
|
file->private_data = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static long smi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
s32 ret = 0;
|
|
|
|
if (!file->f_op || !file->f_op->unlocked_ioctl)
|
|
return -ENOTTY;
|
|
|
|
switch (cmd) {
|
|
case MTK_IOC_SMI_BWC_CONF:
|
|
{
|
|
struct MTK_SMI_BWC_CONF conf;
|
|
|
|
ret = copy_from_user(&conf, (void *)arg, sizeof(conf));
|
|
if (ret)
|
|
SMIWRN(0, "CMD%u copy from user failed:%d\n", cmd, ret);
|
|
else
|
|
ret = smi_bwc_conf(&conf);
|
|
break;
|
|
}
|
|
case MTK_IOC_MMDVFS_QOS_CMD:
|
|
{
|
|
struct MTK_MMDVFS_QOS_CMD config;
|
|
|
|
ret = copy_from_user(&config, (void *)arg,
|
|
sizeof(struct MTK_MMDVFS_QOS_CMD));
|
|
if (ret) {
|
|
SMIWRN(0, "cmd %u copy_from_user fail: %d\n",
|
|
cmd, ret);
|
|
} else {
|
|
switch (config.type) {
|
|
case MTK_MMDVFS_QOS_CMD_TYPE_SET:
|
|
mmdvfs_set_max_camera_hrt_bw(
|
|
config.max_cam_bw);
|
|
config.ret = 0;
|
|
break;
|
|
default:
|
|
SMIWRN(0, "invalid mmdvfs QOS cmd\n");
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
#ifdef MMDVFS_HOOK
|
|
case MTK_IOC_SMI_BWC_INFO_SET:
|
|
ret = set_mm_info_ioctl_wrapper(file, cmd, arg);
|
|
break;
|
|
case MTK_IOC_SMI_BWC_INFO_GET:
|
|
ret = get_mm_info_ioctl_wrapper(file, cmd, arg);
|
|
break;
|
|
case MTK_IOC_MMDVFS_CMD:
|
|
{
|
|
struct MTK_MMDVFS_CMD mmdvfs_cmd;
|
|
|
|
if (copy_from_user(&mmdvfs_cmd,
|
|
(void *)arg, sizeof(struct MTK_MMDVFS_CMD))) {
|
|
ret = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
mmdvfs_handle_cmd(&mmdvfs_cmd);
|
|
|
|
if (copy_to_user((void *)arg,
|
|
(void *)&mmdvfs_cmd, sizeof(struct MTK_MMDVFS_CMD))) {
|
|
ret = -EFAULT;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
ret = -ENOIOCTLCMD;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_COMPAT)
|
|
struct MTK_SMI_COMPAT_BWC_CONFIG {
|
|
compat_int_t scen;
|
|
compat_int_t b_on;
|
|
};
|
|
|
|
struct MTK_SMI_COMPAT_BWC_INFO_SET {
|
|
compat_int_t property;
|
|
compat_int_t value1;
|
|
compat_int_t value2;
|
|
};
|
|
|
|
struct MTK_SMI_COMPAT_BWC_MM_INFO {
|
|
compat_uint_t flag; /* reserved */
|
|
compat_int_t concurrent_profile;
|
|
compat_int_t sensor_size[2];
|
|
compat_int_t video_record_size[2];
|
|
compat_int_t display_size[2];
|
|
compat_int_t tv_out_size[2];
|
|
compat_int_t fps;
|
|
compat_int_t video_encode_codec;
|
|
compat_int_t video_decode_codec;
|
|
compat_int_t hw_ovl_limit;
|
|
};
|
|
|
|
#define COMPAT_MTK_IOC_SMI_BWC_CONFIG \
|
|
_IOW('O', 24, struct MTK_SMI_COMPAT_BWC_CONFIG)
|
|
static int smi_bwc_config_compat_get(
|
|
struct MTK_SMI_BWC_CONF __user *data,
|
|
struct MTK_SMI_COMPAT_BWC_CONFIG __user *data32)
|
|
{
|
|
compat_int_t i;
|
|
int ret;
|
|
|
|
ret = get_user(i, &(data32->scen));
|
|
ret |= put_user(i, &(data->scen));
|
|
ret |= get_user(i, &(data32->b_on));
|
|
ret |= put_user(i, &(data->b_on));
|
|
return ret;
|
|
}
|
|
|
|
#define COMPAT_MTK_IOC_SMI_BWC_INFO_SET \
|
|
_IOWR('O', 28, struct MTK_SMI_COMPAT_BWC_INFO_SET)
|
|
static int smi_bwc_info_compat_set(
|
|
struct MTK_SMI_BWC_INFO_SET __user *data,
|
|
struct MTK_SMI_COMPAT_BWC_INFO_SET __user *data32)
|
|
{
|
|
compat_int_t i;
|
|
int ret;
|
|
|
|
ret = get_user(i, &(data32->property));
|
|
ret |= put_user(i, &(data->property));
|
|
ret |= get_user(i, &(data32->value1));
|
|
ret |= put_user(i, &(data->value1));
|
|
ret |= get_user(i, &(data32->value2));
|
|
ret |= put_user(i, &(data->value2));
|
|
return ret;
|
|
}
|
|
|
|
#define COMPAT_MTK_IOC_SMI_BWC_INFO_GET \
|
|
_IOWR('O', 29, struct MTK_SMI_COMPAT_BWC_MM_INFO)
|
|
static int smi_bwc_info_compat_get(
|
|
struct MTK_SMI_BWC_MM_INFO __user *data,
|
|
struct MTK_SMI_COMPAT_BWC_MM_INFO __user *data32)
|
|
{
|
|
compat_uint_t u;
|
|
compat_int_t p[2];
|
|
compat_int_t i;
|
|
int ret;
|
|
|
|
ret = get_user(u, &(data32->flag));
|
|
ret |= put_user(u, &(data->flag));
|
|
|
|
ret |= copy_from_user(p, &(data32->sensor_size), sizeof(p));
|
|
ret |= copy_to_user(&(data->sensor_size), p, sizeof(p));
|
|
ret |= copy_from_user(p, &(data32->video_record_size), sizeof(p));
|
|
ret |= copy_to_user(&(data->video_record_size), p, sizeof(p));
|
|
ret |= copy_from_user(p, &(data32->display_size), sizeof(p));
|
|
ret |= copy_to_user(&(data->display_size), p, sizeof(p));
|
|
ret |= copy_from_user(p, &(data32->tv_out_size), sizeof(p));
|
|
ret |= copy_to_user(&(data->tv_out_size), p, sizeof(p));
|
|
|
|
ret |= get_user(i, &(data32->concurrent_profile));
|
|
ret |= put_user(i, &(data->concurrent_profile));
|
|
ret |= get_user(i, &(data32->fps));
|
|
ret |= put_user(i, &(data->fps));
|
|
ret |= get_user(i, &(data32->video_encode_codec));
|
|
ret |= put_user(i, &(data->video_encode_codec));
|
|
ret |= get_user(i, &(data32->video_decode_codec));
|
|
ret |= put_user(i, &(data->video_decode_codec));
|
|
ret |= get_user(i, &(data32->hw_ovl_limit));
|
|
ret |= put_user(i, &(data->hw_ovl_limit));
|
|
return ret;
|
|
}
|
|
|
|
static int smi_bwc_info_compat_put(
|
|
struct MTK_SMI_BWC_MM_INFO __user *data,
|
|
struct MTK_SMI_COMPAT_BWC_MM_INFO __user *data32)
|
|
{
|
|
compat_uint_t u;
|
|
compat_int_t p[2];
|
|
compat_int_t i;
|
|
int ret;
|
|
|
|
ret = get_user(u, &(data->flag));
|
|
ret |= put_user(u, &(data32->flag));
|
|
|
|
ret |= copy_from_user(p, &(data->sensor_size), sizeof(p));
|
|
ret |= copy_to_user(&(data32->sensor_size), p, sizeof(p));
|
|
ret |= copy_from_user(p, &(data->video_record_size), sizeof(p));
|
|
ret |= copy_to_user(&(data32->video_record_size), p, sizeof(p));
|
|
ret |= copy_from_user(p, &(data->display_size), sizeof(p));
|
|
ret |= copy_to_user(&(data32->display_size), p, sizeof(p));
|
|
ret |= copy_from_user(p, &(data->tv_out_size), sizeof(p));
|
|
ret |= copy_to_user(&(data32->tv_out_size), p, sizeof(p));
|
|
|
|
ret |= get_user(i, &(data->concurrent_profile));
|
|
ret |= put_user(i, &(data32->concurrent_profile));
|
|
ret |= get_user(i, &(data->fps));
|
|
ret |= put_user(i, &(data32->fps));
|
|
ret |= get_user(i, &(data->video_encode_codec));
|
|
ret |= put_user(i, &(data32->video_encode_codec));
|
|
ret |= get_user(i, &(data->video_decode_codec));
|
|
ret |= put_user(i, &(data32->video_decode_codec));
|
|
ret |= get_user(i, &(data->hw_ovl_limit));
|
|
ret |= put_user(i, &(data32->hw_ovl_limit));
|
|
return ret;
|
|
}
|
|
|
|
static long smi_compat_ioctl(struct file *file, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!file->f_op || !file->f_op->unlocked_ioctl)
|
|
return -ENOTTY;
|
|
|
|
switch (cmd) {
|
|
case COMPAT_MTK_IOC_SMI_BWC_CONFIG:
|
|
{
|
|
struct MTK_SMI_BWC_CONF __user *data;
|
|
struct MTK_SMI_COMPAT_BWC_CONFIG __user *data32;
|
|
|
|
data32 = compat_ptr(arg);
|
|
|
|
if (cmd == MTK_IOC_SMI_BWC_CONF)
|
|
return file->f_op->unlocked_ioctl(file, cmd,
|
|
(unsigned long)data32);
|
|
|
|
data = compat_alloc_user_space(
|
|
sizeof(struct MTK_SMI_BWC_CONF));
|
|
if (!data)
|
|
return -EFAULT;
|
|
|
|
ret = smi_bwc_config_compat_get(data, data32);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return file->f_op->unlocked_ioctl(file,
|
|
MTK_IOC_SMI_BWC_CONF, (unsigned long)data);
|
|
}
|
|
|
|
case COMPAT_MTK_IOC_SMI_BWC_INFO_SET:
|
|
{
|
|
struct MTK_SMI_BWC_INFO_SET __user *data;
|
|
struct MTK_SMI_COMPAT_BWC_INFO_SET __user *data32;
|
|
|
|
data32 = compat_ptr(arg);
|
|
|
|
if (cmd == MTK_IOC_SMI_BWC_INFO_SET)
|
|
return file->f_op->unlocked_ioctl(file, cmd,
|
|
(unsigned long)data32);
|
|
|
|
data = compat_alloc_user_space(
|
|
sizeof(struct MTK_SMI_BWC_INFO_SET));
|
|
if (!data)
|
|
return -EFAULT;
|
|
|
|
ret = smi_bwc_info_compat_set(data, data32);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return file->f_op->unlocked_ioctl(file,
|
|
MTK_IOC_SMI_BWC_INFO_SET, (unsigned long)data);
|
|
}
|
|
|
|
case COMPAT_MTK_IOC_SMI_BWC_INFO_GET:
|
|
{
|
|
struct MTK_SMI_BWC_MM_INFO __user *data;
|
|
struct MTK_SMI_COMPAT_BWC_MM_INFO __user *data32;
|
|
|
|
data32 = compat_ptr(arg);
|
|
|
|
if (cmd == MTK_IOC_SMI_BWC_INFO_GET)
|
|
return file->f_op->unlocked_ioctl(file, cmd,
|
|
(unsigned long)data32);
|
|
|
|
data = compat_alloc_user_space(
|
|
sizeof(struct MTK_SMI_BWC_MM_INFO));
|
|
if (!data)
|
|
return -EFAULT;
|
|
|
|
ret = smi_bwc_info_compat_get(data, data32);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = file->f_op->unlocked_ioctl(file,
|
|
MTK_IOC_SMI_BWC_INFO_GET, (unsigned long)data);
|
|
|
|
return smi_bwc_info_compat_put(data, data32);
|
|
}
|
|
case MTK_IOC_MMDVFS_CMD:
|
|
case MTK_IOC_MMDVFS_QOS_CMD:
|
|
return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)
|
|
compat_ptr(arg));
|
|
default:
|
|
return -ENOIOCTLCMD;
|
|
}
|
|
}
|
|
#else /* #if !IS_ENABLED(CONFIG_COMPAT) */
|
|
#define smi_compat_ioctl NULL
|
|
#endif
|
|
|
|
|
|
static const struct file_operations smi_file_opers = {
|
|
.owner = THIS_MODULE,
|
|
.open = smi_open,
|
|
.release = smi_release,
|
|
.unlocked_ioctl = smi_ioctl,
|
|
.compat_ioctl = smi_compat_ioctl,
|
|
};
|
|
|
|
static inline void smi_subsys_sspm_ipi(const bool ena, const u32 subsys)
|
|
{
|
|
#if IS_ENABLED(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && IS_ENABLED(SMI_SSPM)
|
|
struct smi_ipi_data_s ipi_data;
|
|
s32 ret;
|
|
|
|
spin_lock(&(smi_drv.lock));
|
|
smi_subsys_on = ena ?
|
|
(smi_subsys_on | subsys) : (smi_subsys_on & ~subsys);
|
|
spin_unlock(&(smi_drv.lock));
|
|
|
|
ipi_data.cmd = SMI_IPI_ENABLE;
|
|
ipi_data.u.logger.enable = smi_subsys_on;
|
|
#if IS_ENABLED(SMI_SSPM)
|
|
if (!smi_sspm_ipi_register)
|
|
return;
|
|
|
|
do {
|
|
ret = mtk_ipi_send_compl(&sspm_ipidev, IPIS_C_SMI,
|
|
IPI_SEND_POLLING, &ipi_data,
|
|
sizeof(ipi_data) / SSPM_MBOX_SLOT_SIZE, 2000);
|
|
} while (smi_dram.ackdata);
|
|
/*
|
|
* SMIDBG("ena:%d subsys:%#x smi_subsys_on:%#x ackdata:%d\n",
|
|
* ena, subsys, smi_subsys_on, smi_dram.ackdata);
|
|
*/
|
|
#else
|
|
do {
|
|
ret = sspm_ipi_send_sync(IPI_ID_SMI, IPI_OPT_POLLING, &ipi_data,
|
|
sizeof(ipi_data) / MBOX_SLOT_SIZE, &smi_dram.ackdata, 1);
|
|
} while (smi_dram.ackdata);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static void smi_subsys_after_on(enum subsys_id sys)
|
|
{
|
|
u32 subsys = smi_subsys_to_larbs[sys];
|
|
u32 smi_scen = smi_scen_map[smi_drv.scen];
|
|
s32 i;
|
|
#if IS_ENABLED(CONFIG_MACH_MT6781)
|
|
s32 j;
|
|
#endif
|
|
|
|
if (!subsys)
|
|
return;
|
|
|
|
#if IS_ENABLED(CONFIG_MACH_MT6781)
|
|
for (i = SMI_DEV_NUM - 1; i >= 0; i--) {
|
|
if (subsys & (1 << i)) {
|
|
smi_clk_record(i, true, NULL);
|
|
mtk_smi_clk_enable(smi_dev[i]);
|
|
|
|
//larb enable
|
|
if (smi_dev[i]->comm_reset) {
|
|
//power reset
|
|
for (j = 0; (j < MAX_LARB_FOR_CLAMP)
|
|
&& smi_dev[i]->power_reset_reg[j]; j++) {
|
|
writel(smi_dev[i]->power_reset_value[j],
|
|
smi_dev[i]->power_reset_reg[j]);
|
|
writel(0, smi_dev[i]->power_reset_reg[j]);
|
|
}
|
|
//common reset
|
|
for (j = 0; (j < MAX_LARB_FOR_CLAMP)
|
|
&& smi_dev[i]->comm_reset_reg[j]; j++) {
|
|
writel(smi_dev[i]->comm_reset_value[j],
|
|
smi_dev[i]->comm_reset_reg[j]
|
|
+ SMI_COMMON_CLAMP_EN_CLR);
|
|
}
|
|
}
|
|
//common enable
|
|
for (j = 0; (j < MAX_LARB_FOR_CLAMP)
|
|
&& smi_dev[i]->comm_clamp_value[j]; j++) {
|
|
writel(smi_dev[i]->comm_clamp_value[j],
|
|
smi_dev[i]->base + SMI_COMMON_CLAMP_EN_SET);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MMPROFILE)
|
|
//mmprofile_log(smi_mmp_event[sys], MMPROFILE_FLAG_START);
|
|
#endif
|
|
for (i = SMI_DEV_NUM - 1; i >= 0; i--)
|
|
if (subsys & (1 << i)) {
|
|
#if !IS_ENABLED(CONFIG_MACH_MT6781)
|
|
smi_clk_record(i, true, NULL);
|
|
mtk_smi_clk_enable(smi_dev[i]);
|
|
#endif
|
|
mtk_smi_conf_set(smi_dev[i], smi_scen);
|
|
smi_larb_port_set(smi_dev[i]);
|
|
}
|
|
smi_subsys_sspm_ipi(true, subsys);
|
|
}
|
|
|
|
static void smi_subsys_before_off(enum subsys_id sys)
|
|
{
|
|
u32 subsys = smi_subsys_to_larbs[sys];
|
|
s32 i;
|
|
#if IS_ENABLED(CONFIG_MACH_MT6781)
|
|
s32 j;
|
|
#endif
|
|
|
|
if (!subsys)
|
|
return;
|
|
|
|
smi_subsys_sspm_ipi(false, subsys);
|
|
for (i = 0; i < SMI_DEV_NUM; i++)
|
|
if (subsys & (1 << i)) {
|
|
smi_clk_record(i, false, NULL);
|
|
if ((smi_mm_first & subsys) && sys == SYS_DIS)
|
|
continue;
|
|
#if IS_ENABLED(CONFIG_MACH_MT6885) || IS_ENABLED(CONFIG_MACH_MT6893)
|
|
if ((smi_mm_first & subsys) && sys == SYS_MDP)
|
|
continue;
|
|
#endif
|
|
#if !IS_ENABLED(CONFIG_MACH_MT6781)
|
|
mtk_smi_clk_disable(smi_dev[i]);
|
|
#endif
|
|
}
|
|
smi_mm_first &= ~subsys;
|
|
#if IS_ENABLED(CONFIG_MMPROFILE)
|
|
//mmprofile_log(smi_mmp_event[sys], MMPROFILE_FLAG_END);
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_MACH_MT6781)
|
|
for (i = SMI_DEV_NUM - 1; i >= 0; i--) {
|
|
if (subsys & (1 << i)) {
|
|
if (smi_dev[i]->comm_reset) {
|
|
for (j = 0; (j < MAX_LARB_FOR_CLAMP)
|
|
&& smi_dev[i]->comm_reset_reg[j]; j++) {
|
|
//larb disable
|
|
writel(smi_dev[i]->comm_reset_value[j],
|
|
smi_dev[i]->comm_reset_reg[j] +
|
|
SMI_COMMON_CLAMP_EN_SET);
|
|
}
|
|
}
|
|
mtk_smi_clk_disable(smi_dev[i]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_MACH_MT6785) || IS_ENABLED(CONFIG_MACH_MT6885)
|
|
static void smi_subsys_debug_dump(enum subsys_id sys)
|
|
{
|
|
if (!smi_subsys_to_larbs[sys])
|
|
return;
|
|
smi_debug_bus_hang_detect(0, "ccf-cb");
|
|
}
|
|
#endif
|
|
|
|
static struct pg_callbacks smi_clk_subsys_handle = {
|
|
.after_on = smi_subsys_after_on,
|
|
.before_off = smi_subsys_before_off,
|
|
#if IS_ENABLED(CONFIG_MACH_MT6785) || IS_ENABLED(CONFIG_MACH_MT6885)
|
|
.debug_dump = smi_subsys_debug_dump,
|
|
#endif
|
|
};
|
|
|
|
static s32 smi_conf_get(const u32 id)
|
|
{
|
|
if (id >= SMI_DEV_NUM) {
|
|
SMIDBG("Invalid id:%u, SMI_DEV_NUM=%u\n", id, SMI_DEV_NUM);
|
|
return -EINVAL;
|
|
}
|
|
smi_dev[id] = mtk_smi_dev_get(id);
|
|
if (!smi_dev[id]) {
|
|
SMIDBG("SMI%u no such device or address\n", id);
|
|
return -ENXIO;
|
|
}
|
|
|
|
smi_dev[id]->nr_conf_pairs = smi_conf_pair_num[id];
|
|
smi_dev[id]->conf_pairs = smi_conf_pair[id];
|
|
smi_dev[id]->nr_scen_pairs = smi_scen_pair_num[id];
|
|
smi_dev[id]->scen_pairs = smi_scen_pair[id];
|
|
return 0;
|
|
}
|
|
|
|
s32 smi_register(void)
|
|
{
|
|
dev_t dev_no;
|
|
struct cdev *cdev;
|
|
struct class *class;
|
|
struct device *device;
|
|
struct device_node *of_node;
|
|
struct resource res;
|
|
s32 i;
|
|
|
|
if (smi_reg_first)
|
|
return 0;
|
|
/* device */
|
|
dev_no = MKDEV(MTK_SMI_MAJOR_NUMBER, 0);
|
|
if (alloc_chrdev_region(&dev_no, 0, 1, DEV_NAME))
|
|
SMIERR("Allocate chrdev region failed\n");
|
|
cdev = cdev_alloc();
|
|
if (!cdev)
|
|
SMIERR("Allocate cdev failed\n");
|
|
else {
|
|
cdev_init(cdev, &smi_file_opers);
|
|
cdev->owner = THIS_MODULE;
|
|
cdev->dev = dev_no;
|
|
if (cdev_add(cdev, dev_no, 1))
|
|
SMIERR("Add cdev failed\n");
|
|
}
|
|
class = class_create(THIS_MODULE, DEV_NAME);
|
|
if (IS_ERR(class))
|
|
SMIERR("Create class failed: %ld\n", PTR_ERR(class));
|
|
device = device_create(class, NULL, dev_no, NULL, DEV_NAME);
|
|
if (IS_ERR(device))
|
|
SMIERR("Create device failed: %ld\n", PTR_ERR(device));
|
|
|
|
/* driver */
|
|
spin_lock_init(&(smi_drv.lock));
|
|
smi_drv.scen = SMI_BWC_SCEN_NORMAL;
|
|
memset(&smi_drv.table, 0, sizeof(smi_drv.table));
|
|
memset(&smi_record, 0, sizeof(smi_record));
|
|
smi_drv.table[smi_drv.scen] += 1;
|
|
|
|
/* mmsys */
|
|
smi_conf_get(SMI_LARB_NUM);
|
|
|
|
of_node = of_parse_phandle(
|
|
smi_dev[SMI_LARB_NUM]->dev->of_node, "mmsys_config", 0);
|
|
smi_mmsys_base = (void *)of_iomap(of_node, 0);
|
|
if (!smi_mmsys_base) {
|
|
SMIERR("Unable to parse or iomap mmsys_config\n");
|
|
return -ENOMEM;
|
|
}
|
|
if (of_address_to_resource(of_node, 0, &res))
|
|
return -EINVAL;
|
|
SMIWRN(0, "MMSYS base: VA=%p, PA=%pa\n", smi_mmsys_base, &res.start);
|
|
of_node_put(of_node);
|
|
|
|
/* init */
|
|
spin_lock(&(smi_drv.lock));
|
|
smi_subsys_on = smi_subsys_to_larbs[SYS_DIS];
|
|
#if IS_ENABLED(CONFIG_MACH_MT6885) || IS_ENABLED(CONFIG_MACH_MT6893)
|
|
smi_subsys_on |= smi_subsys_to_larbs[SYS_MDP];
|
|
#endif
|
|
spin_unlock(&(smi_drv.lock));
|
|
for (i = SMI_DEV_NUM - 1; i >= 0; i--) {
|
|
smi_conf_get(i);
|
|
if (smi_subsys_on & (1 << i)) {
|
|
smi_bus_prepare_enable(i, DEV_NAME);
|
|
mtk_smi_conf_set(smi_dev[i], smi_drv.scen);
|
|
smi_larb_port_set(smi_dev[i]);
|
|
}
|
|
}
|
|
smi_mm_first = ~0;
|
|
#ifdef MMDVFS_HOOK
|
|
mmdvfs_init();
|
|
mmdvfs_clks_init(smi_dev[SMI_LARB_NUM]->dev->of_node);
|
|
#endif
|
|
smi_debug_dump_status(false);
|
|
register_pg_callback(&smi_clk_subsys_handle);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(smi_register);
|
|
|
|
s32 smi_get_dev_num(void)
|
|
{
|
|
return SMI_DEV_NUM;
|
|
}
|
|
EXPORT_SYMBOL_GPL(smi_get_dev_num);
|
|
static int smi_dram_open(struct inode *node, struct file *filp)
|
|
{
|
|
return nonseekable_open(node, filp);
|
|
}
|
|
|
|
static ssize_t
|
|
smi_dram_read(struct file *filp, char __user *buf, size_t len, loff_t *pos)
|
|
{
|
|
return !smi_dram.virt ? 0 : simple_read_from_buffer(buf, len, pos,
|
|
smi_dram.virt, smi_dram.size);
|
|
}
|
|
|
|
static const struct file_operations smi_dram_file_opers = {
|
|
.owner = THIS_MODULE,
|
|
.open = smi_dram_open,
|
|
.read = smi_dram_read,
|
|
};
|
|
|
|
static inline void smi_mmp_init(void)
|
|
{
|
|
#if IS_ENABLED(CONFIG_MMPROFILE)
|
|
|
|
//s32 i;
|
|
|
|
//mmp_event smi_mmp;
|
|
|
|
//mmprofile_enable(1);
|
|
//smi_mmp = mmprofile_register_event(MMP_ROOT_EVENT, DEV_NAME);
|
|
//for (i = 0; i < NR_SYSS; i++)
|
|
// if (smi_mmp_name[i])
|
|
// smi_mmp_event[i] =
|
|
// mmprofile_register_event(smi_mmp, smi_mmp_name[i]);
|
|
//mmprofile_enable_event_recursive(smi_mmp, 1);
|
|
//mmprofile_start(1);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
static inline void smi_dram_init(void)
|
|
{
|
|
#if IS_ENABLED(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && IS_ENABLED(SMI_SSPM)
|
|
phys_addr_t phys = sspm_reserve_mem_get_phys(SMI_MEM_ID);
|
|
struct smi_ipi_data_s ipi_data;
|
|
s32 ret;
|
|
|
|
#if IS_ENABLED(SMI_SSPM)
|
|
ret = mtk_ipi_register(&sspm_ipidev, IPIS_C_SMI, NULL, NULL,
|
|
(void *)&smi_dram.ackdata);
|
|
if (ret) {
|
|
SMIERR("mtk_ipi_register:%d failed:%d\n", IPIS_C_SMI, ret);
|
|
return;
|
|
}
|
|
smi_sspm_ipi_register = true;
|
|
#endif
|
|
smi_dram.size = sspm_reserve_mem_get_size(SMI_MEM_ID);
|
|
smi_dram.virt = ioremap_wc(phys, smi_dram.size);
|
|
if (IS_ERR(smi_dram.virt))
|
|
SMIERR("ioremap_wc phys=%pa failed: %ld\n",
|
|
&phys, PTR_ERR(smi_dram.virt));
|
|
|
|
ipi_data.cmd = SMI_IPI_INIT;
|
|
ipi_data.u.ctrl.phys = phys;
|
|
ipi_data.u.ctrl.size = smi_dram.size;
|
|
#if IS_ENABLED(SMI_SSPM)
|
|
ret = mtk_ipi_send_compl(&sspm_ipidev, IPIS_C_SMI, IPI_SEND_POLLING,
|
|
&ipi_data, sizeof(ipi_data) / SSPM_MBOX_SLOT_SIZE, 2000);
|
|
#else
|
|
ret = sspm_ipi_send_sync(IPI_ID_SMI, IPI_OPT_POLLING, &ipi_data,
|
|
sizeof(ipi_data) / MBOX_SLOT_SIZE, &smi_dram.ackdata, 1);
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_ENG_BUILD)
|
|
smi_dram.dump = 1;
|
|
#endif
|
|
ipi_data.cmd = SMI_IPI_ENABLE;
|
|
ipi_data.u.logger.enable = (smi_dram.dump << 31) | smi_subsys_on;
|
|
#if IS_ENABLED(SMI_SSPM)
|
|
ret = mtk_ipi_send_compl(&sspm_ipidev, IPIS_C_SMI, IPI_SEND_POLLING,
|
|
&ipi_data, sizeof(ipi_data) / SSPM_MBOX_SLOT_SIZE, 2000);
|
|
#else
|
|
ret = sspm_ipi_send_sync(IPI_ID_SMI, IPI_OPT_POLLING, &ipi_data,
|
|
sizeof(ipi_data) / MBOX_SLOT_SIZE, &smi_dram.ackdata, 1);
|
|
#endif
|
|
#endif
|
|
smi_dram.node = debugfs_create_file(
|
|
"smi_mon", 0444, NULL, (void *)0, &smi_dram_file_opers);
|
|
if (IS_ERR(smi_dram.node))
|
|
pr_info("debugfs_create_file failed: %ld\n",
|
|
PTR_ERR(smi_dram.node));
|
|
}
|
|
|
|
static s32 __init smi_late_init(void)
|
|
{
|
|
s32 i;
|
|
|
|
smi_mmp_init();
|
|
smi_dram_init();
|
|
for (i = 0; i < SMI_DEV_NUM; i++)
|
|
if (smi_subsys_on & (1 << i))
|
|
smi_bus_disable_unprepare(i, DEV_NAME);
|
|
smi_reg_first = true;
|
|
return 0;
|
|
}
|
|
late_initcall(smi_late_init);
|
|
|
|
int smi_dram_dump_get(char *buf, const struct kernel_param *kp)
|
|
{
|
|
s32 pos = 0;
|
|
|
|
pos += snprintf(buf + pos, PAGE_SIZE - pos, "dump=%u\n", smi_dram.dump);
|
|
SMIDBG("dump=%u\n", smi_dram.dump);
|
|
return pos;
|
|
}
|
|
|
|
int smi_dram_dump_set(const char *val, const struct kernel_param *kp)
|
|
{
|
|
s32 arg = 0, ret;
|
|
|
|
ret = kstrtoint(val, 0, &arg);
|
|
if (ret)
|
|
SMIDBG("Invalid val: %s, ret=%d\n", val, ret);
|
|
#if IS_ENABLED(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && IS_ENABLED(SMI_SSPM)
|
|
else if (arg && !smi_dram.dump) {
|
|
struct smi_ipi_data_s ipi_data;
|
|
|
|
smi_dram.dump = 1;
|
|
ipi_data.cmd = SMI_IPI_ENABLE;
|
|
ipi_data.u.logger.enable =
|
|
(smi_dram.dump << 31) | smi_subsys_on;
|
|
#if IS_ENABLED(SMI_SSPM)
|
|
mtk_ipi_send_compl(&sspm_ipidev, IPIS_C_SMI, IPI_SEND_WAIT,
|
|
&ipi_data, sizeof(ipi_data) / SSPM_MBOX_SLOT_SIZE, 2000);
|
|
#else
|
|
sspm_ipi_send_sync(IPI_ID_SMI, IPI_OPT_WAIT,
|
|
&ipi_data, sizeof(ipi_data) / MBOX_SLOT_SIZE, &ret, 1);
|
|
#endif
|
|
}
|
|
#endif
|
|
SMIDBG("arg=%d, dump=%u\n", arg, smi_dram.dump);
|
|
return ret;
|
|
}
|
|
|
|
static struct kernel_param_ops smi_dram_dump_ops = {
|
|
.get = smi_dram_dump_get,
|
|
.set = smi_dram_dump_set,
|
|
};
|
|
|
|
module_param_cb(smi_dram_dump, &smi_dram_dump_ops, NULL, 0644);
|