kernel_samsung_a53x/drivers/soc/samsung/debug/exynos-adv-tracer-s2d.c
2024-06-15 16:02:09 -03:00

348 lines
8.3 KiB
C
Executable file

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/notifier.h>
#include <soc/samsung/debug-snapshot.h>
#include <soc/samsung/exynos-adv-tracer-ipc.h>
#include <soc/samsung/exynos-pmu-if.h>
#include <soc/samsung/exynos-cpupm.h>
enum s2d_ipc_cmd {
eS2D_IPC_CMD_SET_ALL_BLK = 1,
eS2D_IPC_CMD_GET_ALL_BLK,
eS2D_IPC_CMD_SET_ENABLE,
eS2D_IPC_CMD_GET_ENABLE,
};
struct plugin_s2d_info {
struct adv_tracer_plugin *s2d_dev;
struct device *dev;
unsigned int enable;
unsigned int all_block;
} plugin_s2d;
static int arraydump_done;
static unsigned int pmu_burnin_ctrl = 0;
static int sel_scanmode = -1;
static int dbgsel_sw = -1;
#define DONE_ARRYDUMP 0xADADADAD
void adv_tracer_s2d_scandump(void)
{
if (!pmu_burnin_ctrl || (sel_scanmode < 0) || (dbgsel_sw < 0)) {
dev_err(plugin_s2d.dev, "pmu offset no data\n");
return;
}
exynos_pmu_update(pmu_burnin_ctrl, BIT(sel_scanmode), BIT(sel_scanmode));
dev_info(plugin_s2d.dev, "enter scandump mode!\n");
exynos_pmu_update(pmu_burnin_ctrl, BIT(dbgsel_sw), BIT(dbgsel_sw));
}
int adv_tracer_s2d_arraydump(void)
{
struct adv_tracer_ipc_cmd cmd;
int ret = 0;
u32 cpu_mask;
bitmap_to_arr32(&cpu_mask, cpumask_bits(cpu_possible_mask), 32);
if (!plugin_s2d.s2d_dev)
return -ENODEV;
if (arraydump_done == DONE_ARRYDUMP) {
dev_info(plugin_s2d.dev, "Arraydump already done(0x%x)\n",
cpu_mask);
return -1;
}
arraydump_done = DONE_ARRYDUMP;
cmd.cmd_raw.cmd = EAT_IPC_CMD_ARRAYDUMP;
cmd.cmd_raw.id = ARR_IPC_CMD_ID_KERNEL_ARRAYDUMP;
cmd.buffer[1] = dbg_snapshot_get_item_paddr("log_arrdumppanic");
if (!cmd.buffer[1])
return -ENOMEM;
cmd.buffer[2] = cpu_mask;
dev_info(plugin_s2d.dev, "Start Arraydump (0x%x)\n", cpu_mask);
ret = adv_tracer_ipc_send_data_polling_timeout(EAT_FRM_CHANNEL,
&cmd, EAT_IPC_TIMEOUT * 100);
if (ret < 0)
goto end;
dev_info(plugin_s2d.dev, "Finish Arraydump (0x%x)\n", cmd.buffer[1]);
end:
return ret;
}
EXPORT_SYMBOL(adv_tracer_s2d_arraydump);
int adv_tracer_s2d_get_enable(void)
{
struct adv_tracer_ipc_cmd cmd;
int ret = 0;
cmd.cmd_raw.cmd = eS2D_IPC_CMD_GET_ENABLE;
ret = adv_tracer_ipc_send_data_polling(plugin_s2d.s2d_dev->id,
(struct adv_tracer_ipc_cmd *)&cmd);
if (ret < 0) {
dev_err(plugin_s2d.dev, "ipc can't get enable\n");
return ret;
}
plugin_s2d.enable = cmd.buffer[1];
return 0;
}
EXPORT_SYMBOL(adv_tracer_s2d_get_enable);
int adv_tracer_s2d_set_enable(int en)
{
struct adv_tracer_ipc_cmd cmd;
int ret = 0;
cmd.cmd_raw.cmd = eS2D_IPC_CMD_SET_ENABLE;
cmd.buffer[1] = en;
ret = adv_tracer_ipc_send_data_polling(plugin_s2d.s2d_dev->id, &cmd);
if (ret < 0) {
dev_err(plugin_s2d.dev, "ipc can't enable setting\n");
return ret;
}
plugin_s2d.enable = en;
return 0;
}
EXPORT_SYMBOL(adv_tracer_s2d_set_enable);
static int adv_tracer_s2d_set_blk_info(unsigned int all)
{
struct adv_tracer_ipc_cmd cmd;
int ret = 0;
cmd.cmd_raw.cmd = eS2D_IPC_CMD_SET_ALL_BLK;
cmd.buffer[1] = all;
ret = adv_tracer_ipc_send_data_polling(plugin_s2d.s2d_dev->id, &cmd);
if (ret < 0) {
dev_err(plugin_s2d.dev, "s2d ipc cannot select blk\n");
return ret;
}
plugin_s2d.all_block = all;
return 0;
}
static void adv_tracer_s2d_handler(struct adv_tracer_ipc_cmd *cmd,
unsigned int len)
{
}
static ssize_t s2d_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
unsigned long val = simple_strtoul(buf, NULL, 10);
adv_tracer_s2d_set_enable(!!val);
return size;
}
static ssize_t s2d_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, 10, "%sable\n", plugin_s2d.enable ? "en" : "dis");
}
static ssize_t s2d_block_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
unsigned long val = simple_strtoul(buf, NULL, 16);
adv_tracer_s2d_set_blk_info(!!val);
return size;
}
static ssize_t s2d_block_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, 10, "%s\n", plugin_s2d.all_block ? "all" : "cpu");
}
static DEVICE_ATTR_RW(s2d_enable);
static DEVICE_ATTR_RW(s2d_block);
static struct attribute *adv_tracer_s2d_sysfs_attrs[] = {
&dev_attr_s2d_enable.attr,
&dev_attr_s2d_block.attr,
NULL,
};
ATTRIBUTE_GROUPS(adv_tracer_s2d_sysfs);
static unsigned int pmu_rst_seq;
static unsigned int safe_mode_bit;
static void adv_tracer_set_safe_mode(void)
{
if (!pmu_rst_seq)
return;
exynos_pmu_update(pmu_rst_seq, BIT(safe_mode_bit), BIT(safe_mode_bit));
}
static void adv_tracer_clear_safe_mode(void)
{
if (!pmu_rst_seq)
return;
exynos_pmu_update(pmu_rst_seq, BIT(safe_mode_bit), 0);
}
static int adv_tracer_pm_handler(struct notifier_block *nb, unsigned long mode, void *cmd)
{
switch (mode) {
case DSUPD_ENTER:
adv_tracer_set_safe_mode();
break;
case DSUPD_EXIT:
adv_tracer_clear_safe_mode();
break;
case SICD_ENTER:
adv_tracer_set_safe_mode();
break;
case SICD_EXIT:
adv_tracer_clear_safe_mode();
break;
default:
break;
}
return NOTIFY_OK;
}
static struct notifier_block adv_tracer_pm_blk = {
.notifier_call = adv_tracer_pm_handler,
};
static void adv_tracer_pm_init(struct device *dev)
{
struct device_node *np = dev->of_node;
int ret;
ret = of_property_read_u32(np, "pmu-rst-seq", &pmu_rst_seq);
if (ret)
return;
ret = of_property_read_u32(np, "pmu-rst-seq-safe-mode-bit", &safe_mode_bit);
if (ret)
return;
ret = exynos_cpupm_notifier_register(&adv_tracer_pm_blk);
if (ret) {
dev_info(dev, "%s: cpupm nb register failed (%d)\n", __func__, ret);
return;
}
dbg_snapshot_register_debug_ops(NULL, NULL, NULL,
(void *)adv_tracer_set_safe_mode);
adv_tracer_clear_safe_mode();
dev_info(dev, "%s: safe mode registered(%#x/%u)\n", __func__, pmu_rst_seq, safe_mode_bit);
}
static int adv_tracer_s2d_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct adv_tracer_plugin *s2d = NULL;
int ret;
s2d = devm_kzalloc(&pdev->dev, sizeof(struct adv_tracer_plugin),
GFP_KERNEL);
if (!s2d) {
dev_err(&pdev->dev, "can not allocate mem for s2d\n");
ret = -ENOMEM;
goto err_s2d_info;
}
plugin_s2d.dev = &pdev->dev;
plugin_s2d.s2d_dev = s2d;
if (of_property_read_u32(node, "pmu-burnin-ctrl", &pmu_burnin_ctrl))
dev_err(&pdev->dev, "pmu-burnin-ctrl is no data\n");
if (of_property_read_u32(node, "sel-scanmode-bit", &sel_scanmode))
dev_err(&pdev->dev, "sel-scanmode-bit is no data\n");
if (of_property_read_u32(node, "dbgsel-sw-bit", &dbgsel_sw))
dev_err(&pdev->dev, "dbgsel-sw-bit is no data\n");
ret = adv_tracer_ipc_request_channel(node, adv_tracer_s2d_handler,
&s2d->id, &s2d->len);
if (ret < 0) {
dev_err(&pdev->dev, "s2d ipc request fail(%d)\n", ret);
ret = -ENODEV;
goto err_sysfs_probe;
}
ret = adv_tracer_s2d_get_enable();
if (ret < 0)
goto err_sysfs_probe;
dev_info(&pdev->dev, "S2D %sabled\n", plugin_s2d.enable ? "en" : "dis");
platform_set_drvdata(pdev, s2d);
ret = sysfs_create_groups(&pdev->dev.kobj, adv_tracer_s2d_sysfs_groups);
if (ret) {
dev_err(&pdev->dev, "fail to register sysfs.\n");
return ret;
}
dbg_snapshot_register_debug_ops(NULL,
(void *)adv_tracer_s2d_arraydump,
(void *)adv_tracer_s2d_scandump, NULL);
adv_tracer_pm_init(&pdev->dev);
dev_info(&pdev->dev, "%s successful.\n", __func__);
return 0;
err_sysfs_probe:
devm_kfree(&pdev->dev, s2d);
err_s2d_info:
return ret;
}
static int adv_tracer_s2d_remove(struct platform_device *pdev)
{
struct adv_tracer_plugin *s2d = platform_get_drvdata(pdev);
adv_tracer_ipc_release_channel(s2d->id);
return 0;
}
static const struct of_device_id adv_tracer_s2d_match[] = {
{ .compatible = "samsung,exynos-adv-tracer-s2d", },
{},
};
MODULE_DEVICE_TABLE(of, adv_tracer_s2d_match);
static struct platform_driver adv_tracer_s2d_driver = {
.probe = adv_tracer_s2d_probe,
.remove = adv_tracer_s2d_remove,
.driver = {
.name = "exynos-adv-tracer-s2d",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(adv_tracer_s2d_match),
},
};
module_platform_driver(adv_tracer_s2d_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EXYNOS ADV-TRACER-S2D");