/* * Copyright (c) 2020 Samsung Electronics Co., Ltd. * http://www.samsung.com * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum ehld_ipc_cmd { eEHLD_IPC_CMD_SET_ENABLE, eEHLD_IPC_CMD_GET_ENABLE, eEHLD_IPC_CMD_SET_INTERVAL, eEHLD_IPC_CMD_SET_LOCKUP_WARN_VAL, eEHLD_IPC_CMD_SET_LOCKUP_VAL, eEHLD_IPC_CMD_NOTI_CPU_ON, eEHLD_IPC_CMD_NOTI_CPU_OFF, eEHLD_IPC_CMD_LOCKUP_DETECT_WARN, eEHLD_IPC_CMD_LOCKUP_DETECT_SW, eEHLD_IPC_CMD_LOCKUP_DETECT_HW, eEHLD_IPC_CMD_SET_INIT_VAL, eEHLD_IPC_CMD_SEND_CMD, }; struct plugin_ehld_info { struct adv_tracer_plugin *ehld_dev; unsigned int enable; unsigned int interval; } plugin_ehld; int adv_tracer_ehld_get_enable(void) { struct adv_tracer_ipc_cmd cmd; int ret = 0; cmd.cmd_raw.cmd = eEHLD_IPC_CMD_GET_ENABLE; ret = adv_tracer_ipc_send_data_polling(plugin_ehld.ehld_dev->id, (struct adv_tracer_ipc_cmd *)&cmd); if (ret < 0) { pr_info("ehld-ipc: cannot get enable\n"); return ret; } plugin_ehld.enable = cmd.buffer[1]; pr_info("ehld-ipc: EHLD %sabled\n", plugin_ehld.enable ? "en" : "dis"); return plugin_ehld.enable; } int adv_tracer_ehld_set_enable(int en, bool log) { struct adv_tracer_ipc_cmd cmd; int ret = 0; cmd.cmd_raw.cmd = eEHLD_IPC_CMD_SET_ENABLE; cmd.buffer[1] = en; ret = adv_tracer_ipc_send_data_polling(plugin_ehld.ehld_dev->id, &cmd); if (ret < 0) { pr_err("ehld-ipc: cannot enable setting\n"); return ret; } plugin_ehld.enable = en; if (log) pr_info("ehld-ipc: set EHLD to %sabled\n", plugin_ehld.enable ? "en" : "dis"); return 0; } int adv_tracer_ehld_set_init_val(u32 interval, u32 count, u32 cpu_mask) { struct adv_tracer_ipc_cmd cmd; int ret = 0; if (!interval) return -EINVAL; cmd.cmd_raw.cmd = eEHLD_IPC_CMD_SET_INIT_VAL; cmd.buffer[1] = interval; cmd.buffer[2] = count; cmd.buffer[3] = cpu_mask; ret = adv_tracer_ipc_send_data(plugin_ehld.ehld_dev->id, &cmd); if (ret < 0) { pr_err("ehld-ipc: failed to init value\n"); return ret; } plugin_ehld.interval = interval; pr_info("ehld-ipc: set interval to %u ms\n", interval); return 0; } int adv_tracer_ehld_send_cmd(u32 ccmd, u32 val1, u32 val2, u32 ns_time, bool async) { struct adv_tracer_ipc_cmd cmd; int ret = 0; cmd.cmd_raw.cmd = eEHLD_IPC_CMD_SEND_CMD; cmd.buffer[1] = ccmd; cmd.buffer[2] = val1; cmd.buffer[3] = val2; if (ns_time) { ret = adv_tracer_ipc_send_data_polling_timeout (plugin_ehld.ehld_dev->id, &cmd, ns_time); } else { if (async) ret = adv_tracer_ipc_send_data_async(plugin_ehld.ehld_dev->id, &cmd); else ret = adv_tracer_ipc_send_data(plugin_ehld.ehld_dev->id, &cmd); } if (ret < 0) { pr_err("ehld-ipc: failed to send cmd: 0x%x/0x%x\n", ccmd, val1); return ret; } pr_info("ehld-ipc: send cmd: 0x%x/0x%x\n", ccmd, val1); return 0; } int adv_tracer_ehld_set_interval(u32 interval) { struct adv_tracer_ipc_cmd cmd; int ret = 0; if (!interval) return -EINVAL; cmd.cmd_raw.cmd = eEHLD_IPC_CMD_SET_INTERVAL; cmd.buffer[1] = interval; ret = adv_tracer_ipc_send_data(plugin_ehld.ehld_dev->id, &cmd); if (ret < 0) { pr_err("ehld-ipc: can't set the interval\n"); return ret; } plugin_ehld.interval = interval; pr_err("ehld-ipc: set interval to %u ms\n", interval); return 0; } int adv_tracer_ehld_set_warn_count(u32 count) { struct adv_tracer_ipc_cmd cmd; int ret = 0; if (!count) return -EINVAL; cmd.cmd_raw.cmd = eEHLD_IPC_CMD_SET_LOCKUP_WARN_VAL; cmd.buffer[1] = count; ret = adv_tracer_ipc_send_data(plugin_ehld.ehld_dev->id, &cmd); if (ret < 0) { pr_err("ehld-ipc: can't set lockup warning count\n"); return ret; } pr_info("ehld-ipc: set warning count for %u times\n", count); return 0; } int adv_tracer_ehld_set_lockup_count(u32 count) { struct adv_tracer_ipc_cmd cmd; int ret = 0; if (!count) return 0; cmd.cmd_raw.cmd = eEHLD_IPC_CMD_SET_LOCKUP_VAL; cmd.buffer[1] = count; ret = adv_tracer_ipc_send_data(plugin_ehld.ehld_dev->id, &cmd); if (ret < 0) { pr_err("ehld-ipc: can't set lockup count\n"); return ret; } pr_info("ehld-ipc: set lockup count for %u times\n", count); return 0; } u32 adv_tracer_ehld_get_interval(void) { return plugin_ehld.interval; } int adv_tracer_ehld_noti_cpu_state(int cpu, int en) { struct adv_tracer_ipc_cmd cmd; int ret = 0; if (!ret) return -EINVAL; if (en == true) cmd.cmd_raw.cmd = eEHLD_IPC_CMD_NOTI_CPU_ON; else cmd.cmd_raw.cmd = eEHLD_IPC_CMD_NOTI_CPU_OFF; cmd.buffer[1] = cpu; ret = adv_tracer_ipc_send_data_polling(plugin_ehld.ehld_dev->id, &cmd); if (ret < 0) { pr_err("ehld-ipc: cannot cmd state\n"); return ret; } return 0; } static void adv_tracer_ehld_handler(struct adv_tracer_ipc_cmd *cmd, unsigned int len) { switch (cmd->cmd_raw.cmd) { case eEHLD_IPC_CMD_LOCKUP_DETECT_WARN: pr_err("ehld-ipc: CPU%x is Hardlockup Warning - cnt:0x%x\n", (unsigned int)cmd->buffer[1], (unsigned int)cmd->buffer[2]); ehld_do_action((int)cmd->buffer[1], EHLD_STAT_LOCKUP_WARN); break; case eEHLD_IPC_CMD_LOCKUP_DETECT_HW: pr_err("ehld-ipc: CPU%x is Hardlockup Detected - cnt:0x%x by HW\n", (unsigned int)cmd->buffer[1], (unsigned int)cmd->buffer[2]); ehld_do_action((int)cmd->buffer[1], EHLD_STAT_LOCKUP_HW); break; case eEHLD_IPC_CMD_LOCKUP_DETECT_SW: pr_err("ehld-ipc: CPU%x is Hardlockup Detected - counter:0x%x by SW\n", (unsigned int)cmd->buffer[1], (unsigned int)cmd->buffer[2]); ehld_do_action((int)cmd->buffer[1], EHLD_STAT_LOCKUP_SW); break; } } int adv_tracer_ehld_init(void *p) { struct adv_tracer_plugin *ehld = NULL; struct device_node *np = (struct device_node *)p; int ret = 0; ehld = kzalloc(sizeof(struct adv_tracer_plugin), GFP_KERNEL); if (!ehld) { ret = -ENOMEM; return ret; } ret = adv_tracer_ipc_request_channel(np, (ipc_callback)adv_tracer_ehld_handler, &ehld->id, &ehld->len); if (ret < 0) { pr_err("ehld-ipc: request fail(%d)\n", ret); ret = -ENODEV; kfree(ehld); return ret; } plugin_ehld.ehld_dev = ehld; pr_info("ehld-ipc: %s successful - id:0x%x, len:0x%x\n", __func__, ehld->id, ehld->len); return ret; }