Squashed backup commit (no shallow-clone history issues) of xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream commit 9eece662e) plus all fixes applied to get everpal_defconfig building clean against a modern (GCC 15.2.0) toolchain -- see the previous commit message on the lineage-23.2 branch (also in this repo history is not available here since this is a squashed snapshot; full incremental history lives in the local DL380 checkout and the upstream GitHub remote). Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
759 lines
19 KiB
C
759 lines
19 KiB
C
/*
|
|
* 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();
|
|
ufs_cmd_hlist[ptr].ppn = ppn;
|
|
ufs_cmd_hlist[ptr].region = region;
|
|
ufs_cmd_hlist[ptr].subregion = subregion;
|
|
ufs_cmd_hlist[ptr].resv = resv;
|
|
|
|
/* 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_evt_hist(hba, UFS_EVT_PERF_WARN,
|
|
(u32) ((opcode << 24) |
|
|
(((transfer_len >> 12) & 0xFF) << 16) |
|
|
#if BITS_PER_LONG == 32
|
|
(div_u64(ufs_cmd_hlist[ufs_cmd_ptr].duration, 1000000))));
|
|
#else
|
|
(ufs_cmd_hlist[ufs_cmd_ptr].duration / 1000000)));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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,cmd(req)=0x%X,err(time)=%d,line=%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_evt_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_evt_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_evt_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 = true;
|
|
struct ufs_hba *hba = ufs_mtk_hba;
|
|
struct ufs_mtk_host *host = ufshcd_get_variant(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");
|
|
} else if (op == UFS_CMD_HIST_STOP) {
|
|
atomic_set(&cmd_hist_enabled, 0);
|
|
dev_info(hba->dev, "cmd history off\n");
|
|
} else if (op == UFS_CMD_QOS_ON) {
|
|
if (host && host->qos_allowed) {
|
|
host->qos_enabled = true;
|
|
dev_info(hba->dev, "QoS on\n");
|
|
}
|
|
} else if (op == UFS_CMD_QOS_OFF) {
|
|
if (host && host->qos_allowed) {
|
|
host->qos_enabled = false;
|
|
dev_info(hba->dev, "QoS off\n");
|
|
}
|
|
} else {
|
|
handled = false;
|
|
}
|
|
|
|
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);
|