| // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note |
| /* |
| * |
| * (C) COPYRIGHT 2022-2023 ARM Limited. All rights reserved. |
| * |
| * This program is free software and is provided to you under the terms of the |
| * GNU General Public License version 2 as published by the Free Software |
| * Foundation, and any use by you of this program is subject to the terms |
| * of such GNU license. |
| * |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, you can access it online at |
| * http://www.gnu.org/licenses/gpl-2.0.html. |
| * |
| */ |
| |
| #include <linux/of_platform.h> |
| #include <coresight-priv.h> |
| #include "sources/coresight_mali_sources.h" |
| #include "coresight-ela600.h" |
| |
| /* Linux Coresight framework does not support multiple sources enabled |
| * at the same time. |
| * |
| * To avoid Kernel instability, all Mali Coresight sources use the |
| * same trace ID value as the mandatory ETM one. |
| */ |
| #define CS_MALI_TRACE_ID 0x00000010 |
| |
| #define CS_ELA_BASE_ADDR 0xE0043000 |
| #define CS_GPU_COMMAND_ADDR 0x40003030 |
| #define CS_GPU_COMMAND_TRACE_CONTROL_EN 0x000001DC |
| #define CS_ELA_MAX_SIGNAL_GROUPS 12U |
| #define CS_SG_NAME_MAX_LEN 10U |
| #define CS_ELA_NR_SIG_REGS 8U |
| |
| #define NELEMS(s) (sizeof(s) / sizeof((s)[0])) |
| |
| #define CS_ELA_SIGREGS_ATTR_RW(_a, _b) \ |
| static ssize_t _a##_show(struct device *dev, struct device_attribute *attr, \ |
| char *const buf) \ |
| { \ |
| return sprintf_regs(buf, CS_ELA_##_b##_0, CS_ELA_##_b##_7); \ |
| } \ |
| static ssize_t _a##_store(struct device *dev, struct device_attribute *attr, \ |
| const char *buf, size_t count) \ |
| { \ |
| return verify_store_8_regs(dev, buf, count, CS_ELA_##_b##_0); \ |
| } \ |
| static DEVICE_ATTR_RW(_a) |
| |
| enum cs_ela_dynamic_regs { |
| CS_ELA_TIMECTRL, |
| CS_ELA_TSSR, |
| |
| CS_ELA_SIGSEL0, |
| CS_ELA_COMPCTRL0, |
| CS_ELA_ALTCOMPCTRL0, |
| CS_ELA_TWBSEL0, |
| CS_ELA_QUALMASK0, |
| CS_ELA_QUALCOMP0, |
| CS_ELA_SIGMASK0_0, |
| CS_ELA_SIGMASK0_1, |
| CS_ELA_SIGMASK0_2, |
| CS_ELA_SIGMASK0_3, |
| CS_ELA_SIGMASK0_4, |
| CS_ELA_SIGMASK0_5, |
| CS_ELA_SIGMASK0_6, |
| CS_ELA_SIGMASK0_7, |
| CS_ELA_SIGCOMP0_0, |
| CS_ELA_SIGCOMP0_1, |
| CS_ELA_SIGCOMP0_2, |
| CS_ELA_SIGCOMP0_3, |
| CS_ELA_SIGCOMP0_4, |
| CS_ELA_SIGCOMP0_5, |
| CS_ELA_SIGCOMP0_6, |
| CS_ELA_SIGCOMP0_7, |
| |
| CS_ELA_SIGSEL4, |
| CS_ELA_NEXTSTATE4, |
| CS_ELA_ACTION4, |
| CS_ELA_ALTNEXTSTATE4, |
| CS_ELA_COMPCTRL4, |
| CS_ELA_TWBSEL4, |
| CS_ELA_SIGMASK4_0, |
| CS_ELA_SIGMASK4_1, |
| CS_ELA_SIGMASK4_2, |
| CS_ELA_SIGMASK4_3, |
| CS_ELA_SIGMASK4_4, |
| CS_ELA_SIGMASK4_5, |
| CS_ELA_SIGMASK4_6, |
| CS_ELA_SIGMASK4_7, |
| CS_ELA_SIGCOMP4_0, |
| CS_ELA_SIGCOMP4_1, |
| CS_ELA_SIGCOMP4_2, |
| CS_ELA_SIGCOMP4_3, |
| CS_ELA_SIGCOMP4_4, |
| CS_ELA_SIGCOMP4_5, |
| CS_ELA_SIGCOMP4_6, |
| CS_ELA_SIGCOMP4_7, |
| |
| CS_ELA_NR_DYN_REGS |
| }; |
| |
| enum cs_ela_tracemodes { |
| CS_ELA_TRACEMODE_NONE, |
| CS_ELA_TRACEMODE_JCN, |
| CS_ELA_TRACEMODE_CEU_EXEC, |
| CS_ELA_TRACEMODE_CEU_CMDS, |
| CS_ELA_TRACEMODE_MCU_AHBP, |
| CS_ELA_TRACEMODE_HOST_AXI, |
| CS_ELA_NR_TRACEMODE |
| }; |
| |
| enum cs_ela_signal_types { |
| CS_ELA_SIGTYPE_JCN_REQ, |
| CS_ELA_SIGTYPE_JCN_RES, |
| CS_ELA_SIGTYPE_CEU_EXEC, |
| CS_ELA_SIGTYPE_CEU_CMDS, |
| CS_ELA_SIGTYPE_MCU_AHBP, |
| CS_ELA_SIGTYPE_HOST_AXI, |
| CS_ELA_NR_SIGTYPE, |
| }; |
| |
| struct cs_ela_state { |
| enum cs_ela_tracemodes tracemode; |
| u32 supported_tracemodes; |
| int enabled; |
| u32 signal_types[CS_ELA_NR_SIGTYPE]; |
| u32 regs[CS_ELA_NR_DYN_REGS]; |
| }; |
| |
| #if KERNEL_VERSION(5, 3, 0) <= LINUX_VERSION_CODE |
| static char *type_name = "mali-source-ela"; |
| #endif |
| |
| static struct cs_ela_state ela_state = { 0 }; |
| |
| /* Setup ELA sysfs attributes */ |
| static char *tracemode_names[] = { |
| [CS_ELA_TRACEMODE_NONE] = "NONE", [CS_ELA_TRACEMODE_JCN] = "JCN", |
| [CS_ELA_TRACEMODE_CEU_EXEC] = "CEU_EXEC", [CS_ELA_TRACEMODE_CEU_CMDS] = "CEU_CMDS", |
| [CS_ELA_TRACEMODE_MCU_AHBP] = "MCU_AHBP", [CS_ELA_TRACEMODE_HOST_AXI] = "HOST_AXI", |
| }; |
| |
| static char *signal_type_names[] = { |
| [CS_ELA_SIGTYPE_JCN_REQ] = "jcn-request", [CS_ELA_SIGTYPE_JCN_RES] = "jcn-response", |
| [CS_ELA_SIGTYPE_CEU_EXEC] = "ceu-execution", [CS_ELA_SIGTYPE_CEU_CMDS] = "ceu-commands", |
| [CS_ELA_SIGTYPE_MCU_AHBP] = "mcu-ahbp", [CS_ELA_SIGTYPE_HOST_AXI] = "host-axi", |
| }; |
| |
| static int signal_type_tracemode_map[] = { |
| [CS_ELA_SIGTYPE_JCN_REQ] = CS_ELA_TRACEMODE_JCN, |
| [CS_ELA_SIGTYPE_JCN_RES] = CS_ELA_TRACEMODE_JCN, |
| [CS_ELA_SIGTYPE_CEU_EXEC] = CS_ELA_TRACEMODE_CEU_EXEC, |
| [CS_ELA_SIGTYPE_CEU_CMDS] = CS_ELA_TRACEMODE_CEU_CMDS, |
| [CS_ELA_SIGTYPE_MCU_AHBP] = CS_ELA_TRACEMODE_MCU_AHBP, |
| [CS_ELA_SIGTYPE_HOST_AXI] = CS_ELA_TRACEMODE_HOST_AXI, |
| }; |
| |
| static void setup_tracemode_registers(int tracemode) |
| { |
| switch (tracemode) { |
| case CS_ELA_TRACEMODE_NONE: |
| /* Perform full reset of all dynamic registers */ |
| memset(ela_state.regs, 0x00000000, sizeof(u32) * CS_ELA_NR_DYN_REGS); |
| |
| ela_state.tracemode = CS_ELA_TRACEMODE_NONE; |
| break; |
| case CS_ELA_TRACEMODE_JCN: |
| |
| if (ela_state.signal_types[CS_ELA_SIGTYPE_JCN_REQ] == |
| ela_state.signal_types[CS_ELA_SIGTYPE_JCN_RES]) { |
| ela_state.regs[CS_ELA_TSSR] = 0x00000000; |
| |
| ela_state.regs[CS_ELA_SIGSEL0] = |
| ela_state.signal_types[CS_ELA_SIGTYPE_JCN_REQ]; |
| |
| ela_state.regs[CS_ELA_COMPCTRL0] = 0x00000010; |
| ela_state.regs[CS_ELA_ALTCOMPCTRL0] = 0x00001000; |
| ela_state.regs[CS_ELA_TWBSEL0] = 0x0000FFFF; |
| ela_state.regs[CS_ELA_QUALMASK0] = 0x00000000; |
| ela_state.regs[CS_ELA_QUALCOMP0] = 0x00000000; |
| |
| memset(&ela_state.regs[CS_ELA_SIGMASK0_0], 0x00000000, |
| sizeof(u32) * (CS_ELA_SIGCOMP0_7 - CS_ELA_SIGMASK0_0 + 1)); |
| ela_state.regs[CS_ELA_SIGMASK0_1] = 0x80000000; |
| ela_state.regs[CS_ELA_SIGMASK0_3] = 0x80000000; |
| ela_state.regs[CS_ELA_SIGCOMP0_1] = 0x80000000; |
| ela_state.regs[CS_ELA_SIGCOMP0_3] = 0x80000000; |
| |
| memset(&ela_state.regs[CS_ELA_SIGSEL4], 0x00000000, |
| sizeof(u32) * (CS_ELA_SIGCOMP4_7 - CS_ELA_SIGSEL4 + 1)); |
| |
| ela_state.regs[CS_ELA_COMPCTRL4] = 0x11111111; |
| |
| } else { |
| ela_state.regs[CS_ELA_TSSR] = 0x00000010; |
| |
| ela_state.regs[CS_ELA_SIGSEL0] = |
| ela_state.signal_types[CS_ELA_SIGTYPE_JCN_REQ]; |
| |
| ela_state.regs[CS_ELA_COMPCTRL0] = 0x00000100; |
| ela_state.regs[CS_ELA_ALTCOMPCTRL0] = 0x11111111; |
| ela_state.regs[CS_ELA_TWBSEL0] = 0x00000FFF; |
| ela_state.regs[CS_ELA_QUALMASK0] = 0x00000000; |
| ela_state.regs[CS_ELA_QUALCOMP0] = 0x00000000; |
| |
| memset(&ela_state.regs[CS_ELA_SIGMASK0_0], 0x00000000, |
| sizeof(u32) * (CS_ELA_SIGCOMP0_7 - CS_ELA_SIGMASK0_0 + 1)); |
| ela_state.regs[CS_ELA_SIGMASK0_2] |= 0x80000000; |
| ela_state.regs[CS_ELA_SIGCOMP0_2] |= 0x80000000; |
| |
| ela_state.regs[CS_ELA_SIGSEL4] = |
| ela_state.signal_types[CS_ELA_SIGTYPE_JCN_RES]; |
| ela_state.regs[CS_ELA_NEXTSTATE4] = 0x00000010; |
| ela_state.regs[CS_ELA_ACTION4] = 0x00000008; |
| ela_state.regs[CS_ELA_ALTNEXTSTATE4] = 0x00000001; |
| ela_state.regs[CS_ELA_COMPCTRL4] = 0x00000100; |
| ela_state.regs[CS_ELA_TWBSEL4] = 0x00000FFF; |
| |
| memset(&ela_state.regs[CS_ELA_SIGMASK4_0], 0x00000000, |
| sizeof(u32) * (CS_ELA_SIGCOMP4_7 - CS_ELA_SIGMASK4_0 + 1)); |
| ela_state.regs[CS_ELA_SIGMASK4_2] |= 0x80000000; |
| ela_state.regs[CS_ELA_SIGCOMP4_2] |= 0x80000000; |
| } |
| |
| break; |
| case CS_ELA_TRACEMODE_CEU_EXEC: |
| case CS_ELA_TRACEMODE_CEU_CMDS: |
| ela_state.regs[CS_ELA_TSSR] = 0x00000000; |
| |
| if (tracemode == CS_ELA_TRACEMODE_CEU_EXEC) { |
| ela_state.regs[CS_ELA_SIGSEL0] = |
| ela_state.signal_types[CS_ELA_SIGTYPE_CEU_EXEC]; |
| ela_state.regs[CS_ELA_ALTCOMPCTRL0] = 0x00001000; |
| } else if (tracemode == CS_ELA_TRACEMODE_CEU_CMDS) { |
| ela_state.regs[CS_ELA_SIGSEL0] = |
| ela_state.signal_types[CS_ELA_SIGTYPE_CEU_CMDS]; |
| ela_state.regs[CS_ELA_ALTCOMPCTRL0] = 0x11111111; |
| } |
| |
| ela_state.regs[CS_ELA_COMPCTRL0] = 0x00000001; |
| ela_state.regs[CS_ELA_TWBSEL0] = 0x0000FFFF; |
| ela_state.regs[CS_ELA_QUALMASK0] = 0x0000000F; |
| ela_state.regs[CS_ELA_QUALCOMP0] = 0x0000000F; |
| |
| memset(&ela_state.regs[CS_ELA_SIGMASK0_0], 0x00000000, |
| sizeof(u32) * (CS_ELA_SIGCOMP0_7 - CS_ELA_SIGMASK0_0 + 1)); |
| |
| memset(&ela_state.regs[CS_ELA_SIGSEL4], 0x00000000, |
| sizeof(u32) * (CS_ELA_SIGCOMP4_7 - CS_ELA_SIGSEL4 + 1)); |
| |
| ela_state.regs[CS_ELA_COMPCTRL4] = 0x11111111; |
| |
| break; |
| case CS_ELA_TRACEMODE_MCU_AHBP: |
| case CS_ELA_TRACEMODE_HOST_AXI: |
| ela_state.regs[CS_ELA_TSSR] = 0x00000000; |
| |
| if (tracemode == CS_ELA_TRACEMODE_MCU_AHBP) |
| ela_state.regs[CS_ELA_SIGSEL0] = |
| ela_state.signal_types[CS_ELA_SIGTYPE_MCU_AHBP]; |
| else if (tracemode == CS_ELA_TRACEMODE_HOST_AXI) |
| ela_state.regs[CS_ELA_SIGSEL0] = |
| ela_state.signal_types[CS_ELA_SIGTYPE_HOST_AXI]; |
| |
| ela_state.regs[CS_ELA_COMPCTRL0] = 0x00000001; |
| ela_state.regs[CS_ELA_ALTCOMPCTRL0] = 0x11111111; |
| ela_state.regs[CS_ELA_TWBSEL0] = 0x000000FF; |
| ela_state.regs[CS_ELA_QUALMASK0] = 0x00000003; |
| ela_state.regs[CS_ELA_QUALCOMP0] = 0x00000003; |
| |
| memset(&ela_state.regs[CS_ELA_SIGMASK0_0], 0x00000000, |
| sizeof(u32) * (CS_ELA_SIGCOMP0_7 - CS_ELA_SIGMASK0_0 + 1)); |
| |
| memset(&ela_state.regs[CS_ELA_SIGSEL4], 0x00000000, |
| sizeof(u32) * (CS_ELA_SIGCOMP4_7 - CS_ELA_SIGSEL4 + 1)); |
| |
| ela_state.regs[CS_ELA_COMPCTRL4] = 0x11111111; |
| |
| break; |
| } |
| ela_state.tracemode = tracemode; |
| } |
| |
| static ssize_t select_show(struct device *dev, struct device_attribute *attr, char *const buf) |
| { |
| ssize_t ret = 0; |
| unsigned int mode; |
| |
| for (mode = CS_ELA_TRACEMODE_NONE; mode < CS_ELA_NR_TRACEMODE; mode++) { |
| if (ela_state.supported_tracemodes & (1U << mode)) { |
| if (ela_state.tracemode == mode) |
| ret += sprintf(buf + ret, "[%s]\n", tracemode_names[mode]); |
| else |
| ret += sprintf(buf + ret, "%s\n", tracemode_names[mode]); |
| } |
| } |
| return ret; |
| } |
| |
| static ssize_t select_store(struct device *dev, struct device_attribute *attr, const char *buf, |
| size_t count) |
| { |
| struct coresight_mali_source_drvdata *drvdata = dev_get_drvdata(dev->parent); |
| unsigned int mode = 0; |
| |
| /* Check if enabled and return error */ |
| if (ela_state.enabled == 1) { |
| dev_err(drvdata->base.dev, |
| "Config needs to be disabled before modifying registers"); |
| return -EINVAL; |
| } |
| |
| for (mode = CS_ELA_TRACEMODE_NONE; mode < CS_ELA_NR_TRACEMODE; mode++) { |
| if (sysfs_streq(tracemode_names[mode], buf) && |
| (ela_state.supported_tracemodes & (1U << mode))) { |
| setup_tracemode_registers(mode); |
| return count; |
| } |
| } |
| |
| dev_err(drvdata->base.dev, "Invalid tracemode: %s", buf); |
| return -EINVAL; |
| } |
| |
| static DEVICE_ATTR_RW(select); |
| |
| static ssize_t is_enabled_show(struct device *dev, struct device_attribute *attr, char *const buf) |
| { |
| return sprintf(buf, "%d\n", ela_state.enabled); |
| } |
| |
| static DEVICE_ATTR_RO(is_enabled); |
| |
| static ssize_t sprintf_regs(char *const buf, int from_reg, int to_reg) |
| { |
| ssize_t ret = 0; |
| unsigned int i = 0; |
| |
| for (i = from_reg; i <= to_reg; i++) |
| ret += sprintf(buf + ret, "0x%08X ", ela_state.regs[i]); |
| |
| ret += sprintf(buf + ret, "\n"); |
| return ret; |
| } |
| |
| static ssize_t verify_store_8_regs(struct device *dev, const char *buf, size_t count, int from_reg) |
| { |
| struct coresight_mali_source_drvdata *drvdata = dev_get_drvdata(dev->parent); |
| u32 regs[CS_ELA_NR_SIG_REGS] = { 0 }; |
| int items; |
| unsigned int i; |
| |
| if (ela_state.enabled == 1) { |
| dev_err(drvdata->base.dev, |
| "Config needs to be disabled before modifying registers"); |
| return -EINVAL; |
| } |
| |
| items = sscanf(buf, "%x %x %x %x %x %x %x %x", ®s[0], ®s[1], ®s[2], ®s[3], |
| ®s[4], ®s[5], ®s[6], ®s[7]); |
| if (items <= 0) { |
| dev_err(drvdata->base.dev, "Invalid register value"); |
| return -EINVAL; |
| } |
| if (items != CS_ELA_NR_SIG_REGS) { |
| dev_err(drvdata->base.dev, "Incorrect number of registers set (%d != %d)", items, |
| CS_ELA_NR_SIG_REGS); |
| return -EINVAL; |
| } |
| for (i = 0; i < CS_ELA_NR_SIG_REGS; i++) |
| ela_state.regs[from_reg + i] = regs[i]; |
| |
| return count; |
| } |
| |
| CS_ELA_SIGREGS_ATTR_RW(sigmask0, SIGMASK0); |
| CS_ELA_SIGREGS_ATTR_RW(sigcomp0, SIGCOMP0); |
| CS_ELA_SIGREGS_ATTR_RW(sigmask4, SIGMASK4); |
| CS_ELA_SIGREGS_ATTR_RW(sigcomp4, SIGCOMP4); |
| |
| static struct attribute *coresight_ela_attrs[] = { |
| &dev_attr_select.attr, |
| &dev_attr_is_enabled.attr, |
| &dev_attr_sigmask0.attr, |
| &dev_attr_sigcomp0.attr, |
| &dev_attr_sigmask4.attr, |
| &dev_attr_sigcomp4.attr, |
| NULL, |
| }; |
| |
| static struct attribute_group coresight_ela_group = { |
| .attrs = coresight_ela_attrs, |
| }; |
| |
| static const struct attribute_group *coresight_ela_groups[] = { |
| &coresight_ela_group, |
| NULL, |
| }; |
| |
| const struct attribute_group **coresight_mali_source_groups_get(void) |
| { |
| return coresight_ela_groups; |
| } |
| |
| /* Initialize ELA coresight driver */ |
| |
| static struct kbase_debug_coresight_csf_address_range ela_range[] = { |
| { CS_ELA_BASE_ADDR, CS_ELA_BASE_ADDR + CORESIGHT_DEVTYPE }, |
| { CS_GPU_COMMAND_ADDR, CS_GPU_COMMAND_ADDR } |
| }; |
| |
| static struct kbase_debug_coresight_csf_op ela_enable_ops[] = { |
| /* Clearing CTRL.RUN and the read only CTRL.TRACE_BUSY. */ |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_CTRL, 0x00000000), |
| /* Poll CTRL.TRACE_BUSY until it becomes low to ensure that trace has stopped. */ |
| POLL_OP(CS_ELA_BASE_ADDR + ELA_CTRL, ELA_CTRL_TRACE_BUSY, 0x0), |
| /* 0 for now. TSEN = 1 or TSINT = 8 in future */ |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_TIMECTRL, &ela_state.regs[CS_ELA_TIMECTRL]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_TSSR, &ela_state.regs[CS_ELA_TSSR]), |
| /* ATID[6:0] = 4; valid range 0x1-0x6F, value must be unique and needs to be |
| * known for trace extraction |
| */ |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_ATBCTRL, 0x00000400), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_PTACTION, ELA_ACTION_TRACE), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_AUXCTRL, 0x00000000), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_CNTSEL, 0x00000000), |
| |
| /* Trigger State 0 */ |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGSEL(0), &ela_state.regs[CS_ELA_SIGSEL0]), |
| /* May need to be configurable in future. */ |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_TRIGCTRL(0), 0x00000000), |
| |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_NEXTSTATE(0), 0x00000001), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_ACTION(0), ELA_ACTION_TRACE), |
| |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_ALTNEXTSTATE(0), 0x00000001), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_ALTACTION(0), ELA_ACTION_TRACE), |
| |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_COMPCTRL(0), &ela_state.regs[CS_ELA_COMPCTRL0]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_ALTCOMPCTRL(0), &ela_state.regs[CS_ELA_ALTCOMPCTRL0]), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_COUNTCOMP(0), 0x00000000), |
| |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_TWBSEL(0), &ela_state.regs[CS_ELA_TWBSEL0]), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_EXTMASK(0), 0x00000000), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_EXTCOMP(0), 0x00000000), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_QUALMASK(0), &ela_state.regs[CS_ELA_QUALMASK0]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_QUALCOMP(0), &ela_state.regs[CS_ELA_QUALCOMP0]), |
| |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(0, 0), &ela_state.regs[CS_ELA_SIGMASK0_0]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(0, 1), &ela_state.regs[CS_ELA_SIGMASK0_1]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(0, 2), &ela_state.regs[CS_ELA_SIGMASK0_2]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(0, 3), &ela_state.regs[CS_ELA_SIGMASK0_3]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(0, 4), &ela_state.regs[CS_ELA_SIGMASK0_4]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(0, 5), &ela_state.regs[CS_ELA_SIGMASK0_5]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(0, 6), &ela_state.regs[CS_ELA_SIGMASK0_6]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(0, 7), &ela_state.regs[CS_ELA_SIGMASK0_7]), |
| |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(0, 0), &ela_state.regs[CS_ELA_SIGCOMP0_0]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(0, 1), &ela_state.regs[CS_ELA_SIGCOMP0_1]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(0, 2), &ela_state.regs[CS_ELA_SIGCOMP0_2]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(0, 3), &ela_state.regs[CS_ELA_SIGCOMP0_3]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(0, 4), &ela_state.regs[CS_ELA_SIGCOMP0_4]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(0, 5), &ela_state.regs[CS_ELA_SIGCOMP0_5]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(0, 6), &ela_state.regs[CS_ELA_SIGCOMP0_6]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(0, 7), &ela_state.regs[CS_ELA_SIGCOMP0_7]), |
| |
| WRITE_RANGE_OP(CS_ELA_BASE_ADDR + ELA_SIGSEL(1), CS_ELA_BASE_ADDR + ELA_SIGCOMP(1, 7), |
| 0x00000000), |
| WRITE_RANGE_OP(CS_ELA_BASE_ADDR + ELA_SIGSEL(2), CS_ELA_BASE_ADDR + ELA_SIGCOMP(2, 7), |
| 0x00000000), |
| WRITE_RANGE_OP(CS_ELA_BASE_ADDR + ELA_SIGSEL(3), CS_ELA_BASE_ADDR + ELA_SIGCOMP(3, 7), |
| 0x00000000), |
| |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_COMPCTRL(1), 0x11111111), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_COMPCTRL(2), 0x11111111), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_COMPCTRL(3), 0x11111111), |
| |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_ALTCOMPCTRL(1), 0x11111111), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_ALTCOMPCTRL(2), 0x11111111), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_ALTCOMPCTRL(3), 0x11111111), |
| |
| /* Trigger State 4 */ |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGSEL(4), &ela_state.regs[CS_ELA_SIGSEL4]), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_TRIGCTRL(4), 0x00000000), |
| |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_NEXTSTATE(4), &ela_state.regs[CS_ELA_NEXTSTATE4]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_ACTION(4), &ela_state.regs[CS_ELA_ACTION4]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_ALTNEXTSTATE(4), &ela_state.regs[CS_ELA_ALTNEXTSTATE4]), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_ALTACTION(4), ELA_ACTION_TRACE), |
| |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_COMPCTRL(4), &ela_state.regs[CS_ELA_COMPCTRL4]), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_ALTCOMPCTRL(4), 0x11111111), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_COUNTCOMP(4), 0x00000000), |
| |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_TWBSEL(4), &ela_state.regs[CS_ELA_TWBSEL4]), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_EXTMASK(4), 0x00000000), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_EXTCOMP(4), 0x00000000), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_QUALMASK(4), 0x00000000), |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_QUALCOMP(4), 0x00000000), |
| |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(4, 0), &ela_state.regs[CS_ELA_SIGMASK4_0]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(4, 1), &ela_state.regs[CS_ELA_SIGMASK4_1]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(4, 2), &ela_state.regs[CS_ELA_SIGMASK4_2]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(4, 3), &ela_state.regs[CS_ELA_SIGMASK4_3]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(4, 4), &ela_state.regs[CS_ELA_SIGMASK4_4]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(4, 5), &ela_state.regs[CS_ELA_SIGMASK4_5]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(4, 6), &ela_state.regs[CS_ELA_SIGMASK4_6]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGMASK(4, 7), &ela_state.regs[CS_ELA_SIGMASK4_7]), |
| |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(4, 0), &ela_state.regs[CS_ELA_SIGCOMP4_0]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(4, 1), &ela_state.regs[CS_ELA_SIGCOMP4_1]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(4, 2), &ela_state.regs[CS_ELA_SIGCOMP4_2]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(4, 3), &ela_state.regs[CS_ELA_SIGCOMP4_3]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(4, 4), &ela_state.regs[CS_ELA_SIGCOMP4_4]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(4, 5), &ela_state.regs[CS_ELA_SIGCOMP4_5]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(4, 6), &ela_state.regs[CS_ELA_SIGCOMP4_6]), |
| WRITE_PTR_OP(CS_ELA_BASE_ADDR + ELA_SIGCOMP(4, 7), &ela_state.regs[CS_ELA_SIGCOMP4_7]), |
| |
| WRITE_IMM_OP(CS_GPU_COMMAND_ADDR, CS_GPU_COMMAND_TRACE_CONTROL_EN), |
| |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_CTRL, ELA_CTRL_RUN), |
| |
| BIT_OR_OP(&ela_state.enabled, 0x1), |
| }; |
| |
| static struct kbase_debug_coresight_csf_op ela_disable_ops[] = { |
| WRITE_IMM_OP(CS_ELA_BASE_ADDR + ELA_CTRL, 0x00000000), |
| /* Poll CTRL.TRACE_BUSY until it becomes low to ensure that trace has stopped. */ |
| POLL_OP(CS_ELA_BASE_ADDR + ELA_CTRL, ELA_CTRL_TRACE_BUSY, 0x0), |
| |
| BIT_AND_OP(&ela_state.enabled, 0x0), |
| }; |
| |
| static int parse_signal_groups(struct coresight_mali_source_drvdata *drvdata) |
| { |
| struct device_node *signal_groups = NULL; |
| unsigned int siggrp_idx; |
| |
| if (drvdata->base.dev->of_node) |
| signal_groups = of_get_child_by_name(drvdata->base.dev->of_node, "signal-groups"); |
| |
| if (!signal_groups) { |
| dev_err(drvdata->base.dev, "Failed to find signal groups OF node"); |
| return -EINVAL; |
| } |
| |
| for (siggrp_idx = 0; siggrp_idx < CS_ELA_MAX_SIGNAL_GROUPS; siggrp_idx++) { |
| char buf[CS_SG_NAME_MAX_LEN]; |
| const char *name; |
| struct property *prop; |
| |
| if (scnprintf(buf, CS_SG_NAME_MAX_LEN, "sg%d", siggrp_idx) == 0) |
| dev_err(drvdata->base.dev, |
| "Signal group name %d scnprintf failed unexpectedly", siggrp_idx); |
| return -EINVAL; |
| } |
| |
| of_property_for_each_string(signal_groups, buf, prop, name) { |
| int sig_type; |
| |
| for (sig_type = 0; sig_type < CS_ELA_NR_SIGTYPE; sig_type++) { |
| if (!strncmp(signal_type_names[sig_type], name, |
| strlen(signal_type_names[sig_type]))) { |
| ela_state.signal_types[sig_type] = (1U << siggrp_idx); |
| ela_state.supported_tracemodes |= |
| (1U << signal_type_tracemode_map[sig_type]); |
| } |
| } |
| } |
| } |
| |
| /* Add TRACEMODE_NONE as supported to allow printing */ |
| ela_state.supported_tracemodes |= (1U << CS_ELA_TRACEMODE_NONE); |
| |
| return 0; |
| } |
| |
| int coresight_mali_sources_init_drvdata(struct coresight_mali_source_drvdata *drvdata) |
| { |
| int res = 0; |
| |
| #if KERNEL_VERSION(5, 3, 0) <= LINUX_VERSION_CODE |
| drvdata->type_name = type_name; |
| #endif |
| |
| drvdata->base.kbase_client = kbase_debug_coresight_csf_register( |
| drvdata->base.gpu_dev, ela_range, NELEMS(ela_range)); |
| if (drvdata->base.kbase_client == NULL) { |
| dev_err(drvdata->base.dev, "Registration with full range failed unexpectedly"); |
| return -EINVAL; |
| } |
| |
| drvdata->trcid = CS_MALI_TRACE_ID; |
| |
| drvdata->base.enable_seq.ops = ela_enable_ops; |
| drvdata->base.enable_seq.nr_ops = NELEMS(ela_enable_ops); |
| |
| drvdata->base.disable_seq.ops = ela_disable_ops; |
| drvdata->base.disable_seq.nr_ops = NELEMS(ela_disable_ops); |
| |
| drvdata->base.config = kbase_debug_coresight_csf_config_create( |
| drvdata->base.kbase_client, &drvdata->base.enable_seq, &drvdata->base.disable_seq); |
| if (!drvdata->base.config) { |
| dev_err(drvdata->base.dev, "config create failed unexpectedly"); |
| return -EINVAL; |
| } |
| |
| res = parse_signal_groups(drvdata); |
| if (res) { |
| dev_err(drvdata->base.dev, "Failed to parse signal groups"); |
| return res; |
| } |
| |
| return 0; |
| } |
| |
| void coresight_mali_sources_deinit_drvdata(struct coresight_mali_source_drvdata *drvdata) |
| { |
| if (drvdata->base.config != NULL) |
| kbase_debug_coresight_csf_config_free(drvdata->base.config); |
| |
| if (drvdata->base.kbase_client != NULL) |
| kbase_debug_coresight_csf_unregister(drvdata->base.kbase_client); |
| } |
| |
| static const struct of_device_id mali_source_ids[] = { { .compatible = |
| "arm,coresight-mali-source-ela" }, |
| {} }; |
| |
| static struct platform_driver mali_sources_platform_driver = { |
| .probe = coresight_mali_sources_probe, |
| .remove = coresight_mali_sources_remove, |
| .driver = { |
| .name = "coresight-mali-source-ela", |
| .owner = THIS_MODULE, |
| .of_match_table = mali_source_ids, |
| .suppress_bind_attrs = true, |
| }, |
| }; |
| |
| static int __init mali_sources_init(void) |
| { |
| return platform_driver_register(&mali_sources_platform_driver); |
| } |
| |
| static void __exit mali_sources_exit(void) |
| { |
| platform_driver_unregister(&mali_sources_platform_driver); |
| } |
| |
| module_init(mali_sources_init); |
| module_exit(mali_sources_exit); |
| |
| MODULE_AUTHOR("Arm Ltd."); |
| MODULE_DESCRIPTION("Arm Coresight Mali source ELA"); |
| MODULE_LICENSE("GPL"); |