blob: d9efd71d93401a6838c9d1bd3b50e91d269c06f6 [file] [log] [blame]
/*
* Copyright (C) 2017 MediaTek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) "["KBUILD_MODNAME"]" fmt
#include <linux/atomic.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <mt-plat/mtk_gpt.h>
#include <linux/io.h>
#include <linux/scatterlist.h>
#include "ufs-mtk-dbg.h"
#include "ufs.h"
#include <mt-plat/mtk_boot.h>
#ifndef FPGA_PLATFORM
#ifdef UPMU_READY
#include <mt-plat/upmu_common.h>
#endif
#endif
#ifdef CONFIG_MTK_UFS_DEBUG
#define MAX_UFS_CMD_HLIST_ENTRY_CNT (500)
/* max dump size is 40KB whitch can be adjusted */
#define UFS_AEE_BUFFER_SIZE (100 * 1024)
struct ufs_cmd_hlist_struct ufs_cmd_hlist[MAX_UFS_CMD_HLIST_ENTRY_CNT];
int ufs_cmd_ptr = MAX_UFS_CMD_HLIST_ENTRY_CNT - 1;
int ufs_cmd_cnt;
static spinlock_t ufs_mtk_cmd_dump_lock;
static int ufs_mtk_is_cmd_dump_lock_init;
static atomic_t cmd_hist_enabled;
char ufs_aee_buffer[UFS_AEE_BUFFER_SIZE];
static void ufs_mtk_dbg_dump_feature(struct ufs_hba *hba, struct seq_file *m)
{
UFS_DEVINFO_PROC_MSG(m, hba->dev, "-- Crypto Features ----\n");
UFS_DEVINFO_PROC_MSG(m, hba->dev, "Features = 0x%x\n",
hba->crypto_feature);
UFS_DEVINFO_PROC_MSG(m, hba->dev, " HW-FDE = 0x%x\n",
UFS_CRYPTO_HW_FDE);
UFS_DEVINFO_PROC_MSG(m, hba->dev, " HW-FDE Encrypted = 0x%x\n",
UFS_CRYPTO_HW_FDE_ENCRYPTED);
UFS_DEVINFO_PROC_MSG(m, hba->dev, " HW-FBE = 0x%x\n",
UFS_CRYPTO_HW_FBE);
UFS_DEVINFO_PROC_MSG(m, hba->dev, " HW-FBE Encrypted = 0x%x\n",
UFS_CRYPTO_HW_FBE_ENCRYPTED);
UFS_DEVINFO_PROC_MSG(m, hba->dev, "-----------------------\n");
}
#endif
void ufs_mtk_dbg_stop_trace(struct ufs_hba *hba)
{
#ifdef CONFIG_MTK_UFS_DEBUG
atomic_set(&cmd_hist_enabled, 0);
dev_info(hba->dev, "cmd history off\n");
#endif
}
void ufs_mtk_dbg_start_trace(struct ufs_hba *hba)
{
#ifdef CONFIG_MTK_UFS_DEBUG
atomic_set(&cmd_hist_enabled, 1);
dev_info(hba->dev, "cmd history on\n");
#endif
}
void ufs_mtk_dbg_add_trace(struct ufs_hba *hba,
enum ufs_trace_event event, u32 tag,
u8 lun, u32 transfer_len, sector_t lba, u8 opcode,
unsigned long long ppn, u32 region, u32 subregion, u32 resv)
{
#ifdef CONFIG_MTK_UFS_DEBUG
int ptr;
unsigned long flags;
if (!ufs_mtk_is_cmd_dump_lock_init) {
spin_lock_init(&ufs_mtk_cmd_dump_lock);
ufs_mtk_is_cmd_dump_lock_init = 1;
atomic_set(&cmd_hist_enabled, 1);
}
if (!atomic_read(&cmd_hist_enabled)
&& event != UFS_TRACE_DEBUG_PROC)
return;
spin_lock_irqsave(&ufs_mtk_cmd_dump_lock, flags);
ufs_cmd_ptr++;
if (ufs_cmd_ptr >= MAX_UFS_CMD_HLIST_ENTRY_CNT)
ufs_cmd_ptr = 0;
ptr = ufs_cmd_ptr;
ufs_cmd_hlist[ptr].pid = current->pid;
ufs_cmd_hlist[ptr].event = event;
ufs_cmd_hlist[ptr].tag = tag;
ufs_cmd_hlist[ptr].transfer_len = transfer_len;
ufs_cmd_hlist[ptr].lun = lun;
ufs_cmd_hlist[ptr].lba = lba;
ufs_cmd_hlist[ptr].opcode = opcode;
ufs_cmd_hlist[ptr].time = sched_clock();
ufs_cmd_hlist[ptr].duration = 0;
ufs_cmd_hlist[ptr].rq = NULL;
ufs_cmd_hlist[ptr].cpu = smp_processor_id();
#if defined(CONFIG_UFSHPB)
ufs_cmd_hlist[ptr].ppn = ppn;
ufs_cmd_hlist[ptr].region = region;
ufs_cmd_hlist[ptr].subregion = subregion;
ufs_cmd_hlist[ptr].resv = resv;
#endif
/* keep request pointer to dig out block layer status */
if (((event == UFS_TRACE_SEND) || (event == UFS_TRACE_COMPLETED) ||
(event == UFS_TRACE_GENERIC)) && tag != 0xFF) {
if (hba->lrb[tag].cmd && hba->lrb[tag].cmd->request) {
ufs_cmd_hlist[ptr].rq =
hba->lrb[tag].cmd->request;
ufs_cmd_hlist[ptr].crypted =
hba->lrb[tag].crypto_enable;
ufs_cmd_hlist[ptr].keyslot =
ufs_cmd_hlist[ptr].crypted ?
hba->lrb[tag].crypto_key_slot : 0;
}
}
if (event == UFS_TRACE_COMPLETED) {
if (ufs_cmd_ptr == 0)
ptr = MAX_UFS_CMD_HLIST_ENTRY_CNT - 1;
else
ptr = ufs_cmd_ptr - 1;
while (1) {
if (ufs_cmd_hlist[ptr].tag == tag) {
ufs_cmd_hlist[ufs_cmd_ptr].duration =
sched_clock() - ufs_cmd_hlist[ptr].time;
break;
}
ptr--;
if (ptr < 0)
ptr = MAX_UFS_CMD_HLIST_ENTRY_CNT - 1;
if (ufs_cmd_ptr == 0) {
if (ptr == (MAX_UFS_CMD_HLIST_ENTRY_CNT - 1))
break;
} else {
if (ptr == ufs_cmd_ptr - 1)
break;
}
}
/* Over 1 second, record performance warning */
if (ufs_cmd_hlist[ufs_cmd_ptr].duration >= 1000000000) {
/*
* op code[31:24]
* length(4KB)[23:16]
* duration(ms)[15:0]
*/
ufshcd_update_reg_hist(&hba->ufs_stats.perf_warn,
(u32) ((opcode << 24) |
(((transfer_len >> 12) & 0xFF) << 16) |
(ufs_cmd_hlist[ufs_cmd_ptr].duration / 1000000)));
}
}
ufs_cmd_cnt++;
spin_unlock_irqrestore(&ufs_mtk_cmd_dump_lock, flags);
#endif
}
void ufs_mtk_dbg_dump_trace(char **buff, unsigned long *size,
u32 latest_cnt, struct seq_file *m)
{
#ifdef CONFIG_MTK_UFS_DEBUG
int ptr;
int dump_cnt;
unsigned long flags;
if (!ufs_mtk_is_cmd_dump_lock_init) {
spin_lock_init(&ufs_mtk_cmd_dump_lock);
ufs_mtk_is_cmd_dump_lock_init = 1;
}
spin_lock_irqsave(&ufs_mtk_cmd_dump_lock, flags);
if (ufs_cmd_cnt > MAX_UFS_CMD_HLIST_ENTRY_CNT)
dump_cnt = MAX_UFS_CMD_HLIST_ENTRY_CNT;
else
dump_cnt = ufs_cmd_cnt;
if (latest_cnt)
dump_cnt = min_t(u32, latest_cnt, dump_cnt);
ptr = ufs_cmd_ptr;
SPREAD_PRINTF(buff, size, m,
"[ufs] CMD History: req_cnt=%d, real_cnt=%d, ptr=%d\n",
latest_cnt, dump_cnt, ptr);
while (dump_cnt > 0) {
if ((ufs_cmd_hlist[ptr].event >= UFS_TRACE_UIC_SEND) &&
(ufs_cmd_hlist[ptr].event <=
UFS_TRACE_UIC_CMPL_PWR_CTRL)) {
SPREAD_PRINTF(buff, size, m,
"%3d-u(%d),%5d,%2d,0x%2X,arg1=0x%X,arg2=0x%X,arg3=0x%X,%llu\n",
ptr,
ufs_cmd_hlist[ptr].cpu,
ufs_cmd_hlist[ptr].pid,
ufs_cmd_hlist[ptr].event,
ufs_cmd_hlist[ptr].opcode, /* command */
ufs_cmd_hlist[ptr].tag, /* argument1 */
ufs_cmd_hlist[ptr].transfer_len, /* argument2 */
(u32)ufs_cmd_hlist[ptr].lba, /* argument3 */
(u64)ufs_cmd_hlist[ptr].time
);
} else if (ufs_cmd_hlist[ptr].event == UFS_TRACE_REG_TOGGLE) {
SPREAD_PRINTF(buff, size, m,
"%3d-g(%d),%5d,state=%d,on=%d,%llu\n",
ptr,
ufs_cmd_hlist[ptr].cpu,
ufs_cmd_hlist[ptr].pid,
ufs_cmd_hlist[ptr].tag, /* state */
ufs_cmd_hlist[ptr].transfer_len, /* on or off */
(u64)ufs_cmd_hlist[ptr].time
);
} else if (ufs_cmd_hlist[ptr].event == UFS_TRACE_TM_SEND ||
ufs_cmd_hlist[ptr].event == UFS_TRACE_TM_COMPLETED) {
SPREAD_PRINTF(buff, size, m,
"%3d-t(%d),%5d,%2d,tm=0x%x,t=%2d,lun=0x%x,data=0x%x,%llu\n",
ptr,
ufs_cmd_hlist[ptr].cpu,
ufs_cmd_hlist[ptr].pid,
ufs_cmd_hlist[ptr].event,
ufs_cmd_hlist[ptr].opcode,
ufs_cmd_hlist[ptr].tag,
ufs_cmd_hlist[ptr].lun,
ufs_cmd_hlist[ptr].transfer_len,
(u64)ufs_cmd_hlist[ptr].time
);
} else if (ufs_cmd_hlist[ptr].event == UFS_TRACE_DEV_SEND ||
ufs_cmd_hlist[ptr].event == UFS_TRACE_DEV_COMPLETED) {
SPREAD_PRINTF(buff, size, m,
"%3d-d(%d),%5d,%2d,0x%2x,t=%2d,lun=0x%x,idn=0x%x,idx=0x%x,sel=0x%x,%llu\n",
ptr,
ufs_cmd_hlist[ptr].cpu,
ufs_cmd_hlist[ptr].pid,
ufs_cmd_hlist[ptr].event,
ufs_cmd_hlist[ptr].opcode,
ufs_cmd_hlist[ptr].tag,
ufs_cmd_hlist[ptr].lun,
(u8)ufs_cmd_hlist[ptr].lba & 0xFF,
(u8)(ufs_cmd_hlist[ptr].lba >> 8) & 0xFF,
(u8)(ufs_cmd_hlist[ptr].lba >> 16) & 0xFF,
(u64)ufs_cmd_hlist[ptr].time
);
} else if (ufs_cmd_hlist[ptr].event == UFS_TRACE_GENERIC) {
SPREAD_PRINTF(buff, size, m,
"%3d-G(%d),%5d,%2d,0x%2x,t=%2d,lba=0x%llx,len=%6d,arg1=0x%X,arg2=%d,arg3=%d,%llu\n",
ptr,
ufs_cmd_hlist[ptr].cpu,
ufs_cmd_hlist[ptr].pid,
ufs_cmd_hlist[ptr].event,
ufs_cmd_hlist[ptr].opcode,
ufs_cmd_hlist[ptr].tag,
(long long int)ufs_cmd_hlist[ptr].lba,
ufs_cmd_hlist[ptr].transfer_len,
ufs_cmd_hlist[ptr].region,
ufs_cmd_hlist[ptr].subregion,
ufs_cmd_hlist[ptr].resv,
(u64)ufs_cmd_hlist[ptr].time
);
} else {
SPREAD_PRINTF(buff, size, m,
"%3d-r(%d),%5d,%2d,0x%2x,t=%2d,lun=0x%x,crypt:%d,%d,lba=0x%llx,len=%6d,%llu,\t%llu",
ptr,
ufs_cmd_hlist[ptr].cpu,
ufs_cmd_hlist[ptr].pid,
ufs_cmd_hlist[ptr].event,
ufs_cmd_hlist[ptr].opcode,
ufs_cmd_hlist[ptr].tag,
ufs_cmd_hlist[ptr].lun,
ufs_cmd_hlist[ptr].crypted,
ufs_cmd_hlist[ptr].keyslot,
(long long int)ufs_cmd_hlist[ptr].lba,
ufs_cmd_hlist[ptr].transfer_len,
(u64)ufs_cmd_hlist[ptr].time,
(u64)ufs_cmd_hlist[ptr].duration
);
#if defined(CONFIG_UFSHPB)
if (ufs_cmd_hlist[ptr].opcode == READ_16) {
SPREAD_PRINTF(buff, size, m,
",\tppn=0x%llx,\tregion=0x%x,\tsubregion=0x%x,\tresv=0x%x",
ufs_cmd_hlist[ptr].ppn,
ufs_cmd_hlist[ptr].region,
ufs_cmd_hlist[ptr].subregion,
ufs_cmd_hlist[ptr].resv
);
}
if (ufs_cmd_hlist[ptr].opcode == UFSHPB_WRITE_BUFFER) {
SPREAD_PRINTF(buff, size, m,
",\tlba=0x%llx,\tbuf_len=0x%x",
ufs_cmd_hlist[ptr].ppn,
ufs_cmd_hlist[ptr].region
);
}
#endif
SPREAD_PRINTF(buff, size, m, "\n");
}
dump_cnt--;
ptr--;
if (ptr < 0)
ptr = MAX_UFS_CMD_HLIST_ENTRY_CNT - 1;
}
spin_unlock_irqrestore(&ufs_mtk_cmd_dump_lock, flags);
#endif
}
void ufs_mtk_dme_cmd_log(struct ufs_hba *hba, struct uic_command *ucmd,
enum ufs_trace_event event)
{
#ifdef CONFIG_MTK_UFS_DEBUG
u32 cmd;
if (event == UFS_TRACE_UIC_SEND)
cmd = ucmd->command;
else
cmd = ufshcd_readl(hba, REG_UIC_COMMAND);
ufs_mtk_dbg_add_trace(hba, event,
ufshcd_readl(hba, REG_UIC_COMMAND_ARG_1),
0,
ufshcd_readl(hba, REG_UIC_COMMAND_ARG_2),
ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3),
cmd, 0, 0, 0, 0);
#endif
}
void ufs_mtk_dbg_hang_detect_dump(void)
{
#ifdef CONFIG_MTK_UFS_DEBUG
int boot_type;
boot_type = get_boot_type();
if (boot_type != BOOTDEV_UFS)
return;
/*
* do not touch host to get unipro or mphy information via
* dme commands during exception handling since interrupt
* or preemption may be disabled.
*/
ufshcd_print_host_state(ufs_mtk_hba, 0, NULL, NULL, NULL);
ufs_mtk_dbg_dump_trace(NULL, NULL,
ufs_mtk_hba->nutrs + ufs_mtk_hba->nutrs / 2, NULL);
ufshcd_print_all_err_hist(ufs_mtk_hba, NULL, NULL, NULL);
#endif
}
void ufs_mtk_dbg_proc_dump(struct seq_file *m)
{
#ifdef CONFIG_MTK_UFS_DEBUG
ufs_mtk_dbg_dump_feature(ufs_mtk_hba, m);
/*
* do not touch host to get unipro or mphy information via
* dme commands during exception handling since interrupt
* or preemption may be disabled.
*/
ufshcd_print_host_state(ufs_mtk_hba, 0, m, NULL, NULL);
ufs_mtk_dbg_dump_trace(NULL, NULL,
MAX_UFS_CMD_HLIST_ENTRY_CNT, m);
ufshcd_print_all_err_hist(ufs_mtk_hba, m, NULL, NULL);
#endif
}
void get_ufs_aee_buffer(unsigned long *vaddr, unsigned long *size)
{
#ifdef CONFIG_MTK_UFS_DEBUG
unsigned long free_size = UFS_AEE_BUFFER_SIZE;
char *buff;
if (ufs_mtk_hba == NULL) {
pr_info("====== Null ufs_mtk_hba, dump skipped ======\n");
return;
}
buff = ufs_aee_buffer;
ufshcd_print_host_state(ufs_mtk_hba, 0, NULL, &buff, &free_size);
ufs_mtk_dbg_dump_trace(&buff, &free_size,
MAX_UFS_CMD_HLIST_ENTRY_CNT, NULL);
ufshcd_print_all_err_hist(ufs_mtk_hba,
NULL, &buff, &free_size);
/* retrun start location */
*vaddr = (unsigned long)ufs_aee_buffer;
*size = UFS_AEE_BUFFER_SIZE - free_size;
ufs_mtk_dbg_start_trace(ufs_mtk_hba);
#endif
}
EXPORT_SYMBOL(get_ufs_aee_buffer);
static int ufsdbg_dump_health_desc(struct seq_file *file)
{
#ifdef CONFIG_MTK_UFS_DEBUG
int err = 0;
int buff_len = QUERY_DESC_HEALTH_MAX_SIZE;
u8 desc_buf[QUERY_DESC_HEALTH_MAX_SIZE];
int i;
pm_runtime_get_sync(ufs_mtk_hba->dev);
err = ufshcd_read_health_desc(ufs_mtk_hba, desc_buf, buff_len);
pm_runtime_put_sync(ufs_mtk_hba->dev);
if (err) {
seq_printf(file, "Reading Health Descriptor failed. err = %d\n",
err);
goto out;
}
for (i = 0; i < QUERY_DESC_HEALTH_MAX_SIZE; i++) {
seq_printf(file,
"Health Descriptor[0x%x] = 0x%x\n",
i,
(u8)desc_buf[i]);
}
seq_printf(file,
"Health Descriptor[offset 0x02]: %s = 0x%x\n",
"bPreEOLInfo",
(u8)desc_buf[2]);
seq_printf(file,
"Health Descriptor[offset 0x03]: %s = 0x%x\n",
"bDeviceLifeTimeEstA",
(u8)desc_buf[3]);
seq_printf(file,
"Health Descriptor[offset 0x04]: %s = 0x%x\n",
"bDeviceLifeTimeEstB",
(u8)desc_buf[4]);
out:
return err;
#else
return 0;
#endif
}
static char cmd_buf[256];
static int ufs_help_proc_show(struct seq_file *m, void *v)
{
seq_puts(m, "\n===============[ufs_help]================\n");
seq_printf(m, "\n Commands dump: echo %x [host_id] > ufs_debug\n",
UFS_CMDS_DUMP);
seq_printf(m, "\n Get Power Mode Status: echo %x [host_id] > ufs_debug\n",
UFS_GET_PWR_MODE);
seq_printf(m, "\n Dump Health Descriptors: echo %x [host_id] > ufs_debug\n",
UFS_DUMP_HEALTH_DESCRIPTOR);
seq_puts(m, "\n NOTE: All input data is Hex number!\n");
seq_puts(m, "\n=============================================\n\n");
return 0;
}
/* ========== driver proc interface =========== */
static int ufs_debug_proc_show(struct seq_file *m, void *v)
{
unsigned long cmd;
seq_printf(m, "ufsdbg: debug command: %s\n", cmd_buf);
if (kstrtoul(cmd_buf, 10, &cmd))
cmd = UFS_CMDS_DUMP;
cmd_buf[0] = '\0';
if (cmd == UFS_CMDS_DUMP) {
/*
* Default print cmd history for aee:
* JE/NE/ANR/EE/SWT/system api dump
*/
seq_puts(m, "==== UFS Debug Info ====\n");
ufs_mtk_dbg_proc_dump(m);
} else if (cmd == UFS_GET_PWR_MODE) {
seq_puts(m, "(1:FAST 2:SLOW 4:FAST_AUTO 5:SLOW_AUTO 7:UNCHANGE)\n");
seq_printf(m, "Power Mode: tx 0x%x rx 0x%x\n",
ufs_mtk_hba->pwr_info.pwr_tx,
ufs_mtk_hba->pwr_info.pwr_rx);
seq_printf(m, "Gear: tx 0x%x rx 0x%x\n",
ufs_mtk_hba->pwr_info.gear_tx,
ufs_mtk_hba->pwr_info.gear_rx);
seq_printf(m, "HS Rate: 0x%x (1:HS_A 2:HS_B)\n",
ufs_mtk_hba->pwr_info.hs_rate);
seq_printf(m, "Lanes: tx 0x%x rx 0x%x\n",
ufs_mtk_hba->pwr_info.lane_tx,
ufs_mtk_hba->pwr_info.lane_rx);
} else if (cmd == UFS_DUMP_HEALTH_DESCRIPTOR) {
ufsdbg_dump_health_desc(m);
}
return 0;
}
static ssize_t ufs_debug_proc_write(struct file *file, const char *buf,
size_t count, loff_t *data)
{
unsigned long op = UFS_CMD_UNKNOWN;
bool handled = false;
struct ufs_hba *hba = ufs_mtk_hba;
if (count == 0 || count > 255)
return -EINVAL;
if (copy_from_user(cmd_buf, buf, count))
return -EINVAL;
/*
* Let's handle simple commands here.
* Simple command can be executed immediately
* after command is written and do not need further
* "read" or "cat" anymore.
*/
cmd_buf[count] = '\0';
if (kstrtoul(cmd_buf, 10, &op))
return -EINVAL;
if (op == UFS_CMD_HIST_BEGIN) {
atomic_set(&cmd_hist_enabled, 1);
dev_info(hba->dev, "cmd history on\n");
handled = true;
} else if (op == UFS_CMD_HIST_STOP) {
atomic_set(&cmd_hist_enabled, 0);
dev_info(hba->dev, "cmd history off\n");
handled = true;
}
ufs_mtk_dbg_add_trace(hba,
UFS_TRACE_DEBUG_PROC,
op, 0, atomic_read(&cmd_hist_enabled),
0, 0, 0, 0, 0, 0);
if (handled)
cmd_buf[0] = '\0';
return count;
}
static int ufs_perf_proc_show(struct seq_file *m, void *v)
{
struct ufs_mtk_host *host = m->private;
seq_puts(m, "UFS Performance Mode\n");
seq_printf(m, " - supported: %d\n", ufs_mtk_perf_is_supported(host));
seq_printf(m, " - enabled: %d\n", host->perf_en);
seq_printf(m, " - mode: %d\n", host->perf_mode);
seq_printf(m, " - crypto_vcore_opp: %d\n", host->crypto_vcore_opp);
return 0;
}
static ssize_t ufs_perf_proc_write(struct file *file, const char *ubuf,
size_t count, loff_t *data)
{
struct ufs_mtk_host *host = PDE_DATA(file->f_mapping->host);
unsigned long op = UFS_CMD_UNKNOWN;
char cmd[16] = {0};
loff_t buff_pos = 0;
int ret = 0, last_mode;
ret = simple_write_to_buffer(cmd, 15, &buff_pos, ubuf, count);
if (ret < 0) {
dev_info(host->hba->dev, "%s: failed to read user data\n",
__func__);
return -EINVAL;
}
cmd[ret] = '\0';
if (kstrtoul(cmd, 10, &op))
return -EINVAL;
last_mode = host->perf_mode;
if (op == PERF_AUTO) {
host->perf_mode = PERF_AUTO;
ret = 0;
} else if (op == PERF_FORCE_ENABLE &&
host->perf_mode != PERF_FORCE_ENABLE) {
host->perf_mode = PERF_FORCE_ENABLE;
ret = ufs_mtk_perf_setup_crypto_clk(host, true);
} else if (op == PERF_FORCE_DISABLE &&
host->perf_mode != PERF_FORCE_DISABLE) {
host->perf_mode = PERF_FORCE_DISABLE;
ret = ufs_mtk_perf_setup_crypto_clk(host, false);
} else
return -EINVAL;
if (ret)
host->perf_mode = last_mode;
return count;
}
static int ufs_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, ufs_debug_proc_show, inode->i_private);
}
static const struct file_operations ufs_proc_fops = {
.open = ufs_proc_open,
.write = ufs_debug_proc_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int ufs_help_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, ufs_help_proc_show, inode->i_private);
}
static const struct file_operations ufs_help_fops = {
.open = ufs_help_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int ufs_perf_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, ufs_perf_proc_show, PDE_DATA(inode));
}
static const struct file_operations ufs_perf_fops = {
.open = ufs_perf_proc_open,
.write = ufs_perf_proc_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#ifndef USER_BUILD_KERNEL
#define PROC_PERM 0660
#else
#define PROC_PERM 0440
#endif
int ufs_mtk_debug_proc_init(struct ufs_hba *hba)
{
struct ufs_mtk_host *host;
struct proc_dir_entry *prEntry;
kuid_t uid;
kgid_t gid;
if (!hba || !hba->priv) {
pr_info("%s: NULL host, exiting\n", __func__);
return -EINVAL;
}
host = hba->priv;
uid = make_kuid(&init_user_ns, 0);
gid = make_kgid(&init_user_ns, 1001);
prEntry = proc_create("ufs_debug", PROC_PERM, NULL, &ufs_proc_fops);
if (prEntry)
proc_set_user(prEntry, uid, gid);
else
pr_info("%s: failed to create /proc/ufs_debug\n", __func__);
prEntry = proc_create("ufs_help", PROC_PERM, NULL, &ufs_help_fops);
if (!prEntry)
pr_info("%s: failed to create /proc/ufs_help\n", __func__);
/* allow write permission in all builds */
prEntry = proc_create_data("ufs_perf", 0660, NULL, &ufs_perf_fops,
host);
if (prEntry)
proc_set_user(prEntry, uid, gid);
else
pr_info("%s: failed to create /proc/ufs_perf\n", __func__);
return 0;
}
EXPORT_SYMBOL_GPL(ufs_mtk_debug_proc_init);