diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu new file mode 100644 index 0000000000000..a6b6019c8ef17 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu @@ -0,0 +1,51 @@ +What: /sys/bus/amba/devices//enable_tgu +Date: April 2026 +KernelVersion: 7.1 +Contact: Jinlong Mao , Songwei Chai +Description: + (RW) Set/Get the enable/disable status of TGU + Accepts only one of the 2 values - 0 or 1. + 0 : disable TGU. + 1 : enable TGU. + +What: /sys/bus/amba/devices//step[0:7]_priority[0:3]/reg[0:17] +Date: April 2026 +KernelVersion: 7.1 +Contact: Jinlong Mao , Songwei Chai +Description: + (RW) Set/Get the sensed signal with specific step and priority for TGU. + +What: /sys/bus/amba/devices//step[0:7]_condition_decode/reg[0:3] +Date: April 2026 +KernelVersion: 7.1 +Contact: Jinlong Mao , Songwei Chai +Description: + (RW) Set/Get the decode mode with specific step for TGU. + +What: /sys/bus/amba/devices//step[0:7]_condition_select/reg[0:3] +Date: April 2026 +KernelVersion: 7.1 +Contact: Jinlong Mao , Songwei Chai +Description: + (RW) Set/Get the next action with specific step for TGU. + +What: /sys/bus/amba/devices//step[0:7]_timer/reg[0:1] +Date: April 2026 +KernelVersion: 7.1 +Contact: Jinlong Mao , Songwei Chai +Description: + (RW) Set/Get the timer value with specific step for TGU. + +What: /sys/bus/amba/devices//step[0:7]_counter/reg[0:1] +Date: April 2026 +KernelVersion: 7.1 +Contact: Jinlong Mao , Songwei Chai +Description: + (RW) Set/Get the counter value with specific step for TGU. + +What: /sys/bus/amba/devices//reset_tgu +Date: April 2026 +KernelVersion: 7.1 +Contact: Jinlong Mao , Songwei Chai +Description: + (Write) Write 1 to reset the dataset for TGU. diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tgu b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tgu deleted file mode 100644 index 4ff5ec9be733e..0000000000000 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tgu +++ /dev/null @@ -1,51 +0,0 @@ -What: /sys/bus/coresight/devices//enable_tgu -Date: July 2025 -KernelVersion 6.16 -Contact: Jinlong Mao (QUIC) , Songwei Chai (QUIC) -Description: - (RW) Set/Get the enable/disable status of TGU - Accepts only one of the 2 values - 0 or 1. - 0 : disable TGU. - 1 : enable TGU. - -What: /sys/bus/coresight/devices//step[0:7]_priority[0:3]/reg[0:17] -Date: July 2025 -KernelVersion 6.16 -Contact: Jinlong Mao (QUIC) , Songwei Chai (QUIC) -Description: - (RW) Set/Get the sensed signal with specific step and priority for TGU. - -What: /sys/bus/coresight/devices//step[0:7]_condition_decode/reg[0:3] -Date: July 2025 -KernelVersion 6.16 -Contact: Jinlong Mao (QUIC) , Songwei Chai (QUIC) -Description: - (RW) Set/Get the decode mode with specific step for TGU. - -What: /sys/bus/coresight/devices//step[0:7]_condition_select/reg[0:3] -Date: July 2025 -KernelVersion 6.16 -Contact: Jinlong Mao (QUIC) , Songwei Chai (QUIC) -Description: - (RW) Set/Get the next action with specific step for TGU. - -What: /sys/bus/coresight/devices//step[0:7]_timer/reg[0:1] -Date: July 2025 -KernelVersion 6.16 -Contact: Jinlong Mao (QUIC) , Songwei Chai (QUIC) -Description: - (RW) Set/Get the timer value with specific step for TGU. - -What: /sys/bus/coresight/devices//step[0:7]_counter/reg[0:1] -Date: July 2025 -KernelVersion 6.16 -Contact: Jinlong Mao (QUIC) , Songwei Chai (QUIC) -Description: - (RW) Set/Get the counter value with specific step for TGU. - -What: /sys/bus/coresight/devices//reset_tgu -Date: July 2025 -KernelVersion 6.16 -Contact: Jinlong Mao (QUIC) , Songwei Chai (QUIC) -Description: - (Write) Write 1 to reset the dataset for TGU. diff --git a/Documentation/devicetree/bindings/arm/qcom,coresight-tgu.yaml b/Documentation/devicetree/bindings/arm/qcom,tgu.yaml similarity index 61% rename from Documentation/devicetree/bindings/arm/qcom,coresight-tgu.yaml rename to Documentation/devicetree/bindings/arm/qcom,tgu.yaml index 3576d38711261..76440f2497b97 100644 --- a/Documentation/devicetree/bindings/arm/qcom,coresight-tgu.yaml +++ b/Documentation/devicetree/bindings/arm/qcom,tgu.yaml @@ -2,7 +2,7 @@ # Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. %YAML 1.2 --- -$id: http://devicetree.org/schemas/arm/qcom,coresight-tgu.yaml# +$id: http://devicetree.org/schemas/arm/qcom,tgu.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Trigger Generation Unit - TGU @@ -18,12 +18,12 @@ description: | preprogrammed state/sequence and then produce a trigger or interrupt. The primary use case of the TGU is to detect patterns or sequences on a - given set of signals within some region to indentify the issue in time + given set of signals within some region to identify the issue in time once there is abnormal behavior in the subsystem. maintainers: - - Mao Jinlong - - Sam Chai + - Mao Jinlong + - Songwei Chai # Need a custom select here or 'arm,primecell' will match on lots of nodes select: @@ -31,14 +31,14 @@ select: compatible: contains: enum: - - qcom,coresight-tgu + - qcom,tgu required: - compatible properties: compatible: items: - - const: qcom,coresight-tgu + - const: qcom,tgu - const: arm,primecell reg: @@ -51,19 +51,6 @@ properties: items: - const: apb_pclk - in-ports: - $ref: /schemas/graph.yaml#/properties/ports - additionalProperties: false - - properties: - port: - description: - The port mechanism here ensures the relationship between TGU and - TPDM, as TPDM is one of the inputs for TGU. It will allow TGU to - function as TPDM's helper and enable TGU when the connected - TPDM is enabled. - $ref: /schemas/graph.yaml#/properties/port - required: - compatible - reg @@ -75,18 +62,10 @@ additionalProperties: false examples: - | tgu@10b0e000 { - compatible = "qcom,coresight-tgu", "arm,primecell"; + compatible = "qcom,tgu", "arm,primecell"; reg = <0x10b0e000 0x1000>; clocks = <&aoss_qmp>; clock-names = "apb_pclk"; - - in-ports { - port { - tgu_in_tpdm_swao: endpoint{ - remote-endpoint = <&tpdm_swao_out_tgu>; - }; - }; - }; }; ... diff --git a/arch/arm64/configs/qcom.config b/arch/arm64/configs/qcom.config index 567bc0c34a784..c7dbcdfd0383f 100644 --- a/arch/arm64/configs/qcom.config +++ b/arch/arm64/configs/qcom.config @@ -16,7 +16,7 @@ CONFIG_CORESIGHT_CORESIGHT_TNOC=m CONFIG_CORESIGHT_CTCU=m CONFIG_CORESIGHT_DUMMY=m CONFIG_CORESIGHT_SOURCE_ETM4X=m -CONFIG_CORESIGHT_TGU=m +CONFIG_QCOM_TGU=m CONFIG_CORESIGHT_TPDM=m CONFIG_CPU_IDLE_THERMAL=y # CONFIG_DEBUG_INFO_REDUCED is not set diff --git a/drivers/Makefile b/drivers/Makefile index 8e1ffa4358d5f..acade03f59425 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -176,6 +176,7 @@ obj-$(CONFIG_RAS) += ras/ obj-$(CONFIG_USB4) += thunderbolt/ obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/ obj-y += hwtracing/intel_th/ +obj-y += hwtracing/qcom/ obj-$(CONFIG_STM) += hwtracing/stm/ obj-$(CONFIG_HISI_PTT) += hwtracing/ptt/ obj-y += android/ diff --git a/drivers/hwtracing/Kconfig b/drivers/hwtracing/Kconfig index 911ee977103c0..8a640218eed84 100644 --- a/drivers/hwtracing/Kconfig +++ b/drivers/hwtracing/Kconfig @@ -7,4 +7,6 @@ source "drivers/hwtracing/intel_th/Kconfig" source "drivers/hwtracing/ptt/Kconfig" +source "drivers/hwtracing/qcom/Kconfig" + endmenu diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 5b0974e0e942b..6a4239ebb582e 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -20,17 +20,6 @@ menuconfig CORESIGHT module will be called coresight. if CORESIGHT -config CORESIGHT_TGU - tristate "CoreSight Trigger Generation Unit driver" - help - This driver provides support for Trigger Generation Unit that is - used to detect patterns or sequences on a given set of signals. - TGU is used to monitor a particular bus within a given region to - detect illegal transaction sequences or slave responses. It is also - used to monitor a data stream to detect protocol violations and to - provide a trigger point for centering data around a specific event - within the trace data buffer. - config CORESIGHT_LINKS_AND_SINKS tristate "CoreSight Link and Sink drivers" help diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index e5f143f91d698..821a1b06b20c2 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -54,7 +54,6 @@ coresight-cti-y := coresight-cti-core.o coresight-cti-platform.o \ coresight-cti-sysfs.o obj-$(CONFIG_ULTRASOC_SMB) += ultrasoc-smb.o obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o -obj-$(CONFIG_CORESIGHT_TGU) += coresight-tgu.o obj-$(CONFIG_CORESIGHT_CTCU) += coresight-ctcu.o coresight-ctcu-y := coresight-ctcu-core.o coresight-ctcu-byte-cntr.o obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) += coresight-kunit-tests.o diff --git a/drivers/hwtracing/coresight/coresight-tgu.c b/drivers/hwtracing/coresight/coresight-tgu.c deleted file mode 100644 index 35c97f31639e3..0000000000000 --- a/drivers/hwtracing/coresight/coresight-tgu.c +++ /dev/null @@ -1,776 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "coresight-priv.h" -#include "coresight-tgu.h" - -DEFINE_CORESIGHT_DEVLIST(tgu_devs, "tgu"); - -static int calculate_array_location(struct tgu_drvdata *drvdata, - int step_index, int operation_index, - int reg_index) -{ - int ret = -EINVAL; - - switch (operation_index) { - case TGU_PRIORITY0: - case TGU_PRIORITY1: - case TGU_PRIORITY2: - case TGU_PRIORITY3: - ret = operation_index * (drvdata->max_step) * - (drvdata->max_reg) + - step_index * (drvdata->max_reg) + reg_index; - break; - case TGU_CONDITION_DECODE: - ret = step_index * (drvdata->max_condition_decode) + - reg_index; - break; - case TGU_CONDITION_SELECT: - ret = step_index * (drvdata->max_condition_select) + reg_index; - break; - case TGU_COUNTER: - ret = step_index * (drvdata->max_counter) + reg_index; - break; - case TGU_TIMER: - ret = step_index * (drvdata->max_timer) + reg_index; - break; - default: - break; - } - return ret; -} - -static int check_array_location(struct tgu_drvdata *drvdata, int step, - int ops, int reg) -{ - int result = calculate_array_location(drvdata, step, ops, reg); - - if (result == -EINVAL) - dev_err(&drvdata->csdev->dev, "%s - Fail\n", __func__); - return result; -} - -static ssize_t tgu_dataset_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent); - struct tgu_attribute *tgu_attr = - container_of(attr, struct tgu_attribute, attr); - int ret = 0; - - ret = check_array_location(drvdata, tgu_attr->step_index, - tgu_attr->operation_index, tgu_attr->reg_num); - if (ret == -EINVAL) - return ret; - - switch (tgu_attr->operation_index) { - case TGU_PRIORITY0: - case TGU_PRIORITY1: - case TGU_PRIORITY2: - case TGU_PRIORITY3: - return sysfs_emit(buf, "0x%x\n", - drvdata->value_table->priority[calculate_array_location( - drvdata, tgu_attr->step_index, - tgu_attr->operation_index, - tgu_attr->reg_num)]); - case TGU_CONDITION_DECODE: - return sysfs_emit(buf, "0x%x\n", - drvdata->value_table->condition_decode[calculate_array_location( - drvdata, tgu_attr->step_index, - tgu_attr->operation_index, - tgu_attr->reg_num)]); - case TGU_CONDITION_SELECT: - return sysfs_emit(buf, "0x%x\n", - drvdata->value_table->condition_select[calculate_array_location( - drvdata, tgu_attr->step_index, - tgu_attr->operation_index, - tgu_attr->reg_num)]); - case TGU_TIMER: - return sysfs_emit(buf, "0x%x\n", - drvdata->value_table->timer[calculate_array_location( - drvdata, tgu_attr->step_index, tgu_attr->operation_index, - tgu_attr->reg_num)]); - case TGU_COUNTER: - return sysfs_emit(buf, "0x%x\n", - drvdata->value_table->counter[calculate_array_location( - drvdata, tgu_attr->step_index, tgu_attr->operation_index, - tgu_attr->reg_num)]); - default: - break; - } - return -EINVAL; -} - -static ssize_t tgu_dataset_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t size) -{ - unsigned long val; - int ret = -EINVAL; - - struct tgu_drvdata *tgu_drvdata = dev_get_drvdata(dev->parent); - struct tgu_attribute *tgu_attr = - container_of(attr, struct tgu_attribute, attr); - - if (kstrtoul(buf, 0, &val)) - return ret; - - ret = check_array_location(tgu_drvdata, tgu_attr->step_index, - tgu_attr->operation_index, tgu_attr->reg_num); - - if (ret == -EINVAL) - return ret; - - guard(spinlock)(&tgu_drvdata->spinlock); - switch (tgu_attr->operation_index) { - case TGU_PRIORITY0: - case TGU_PRIORITY1: - case TGU_PRIORITY2: - case TGU_PRIORITY3: - tgu_drvdata->value_table->priority[calculate_array_location( - tgu_drvdata, tgu_attr->step_index, - tgu_attr->operation_index, - tgu_attr->reg_num)] = val; - ret = size; - break; - case TGU_CONDITION_DECODE: - tgu_drvdata->value_table->condition_decode[calculate_array_location( - tgu_drvdata, tgu_attr->step_index, - tgu_attr->operation_index, - tgu_attr->reg_num)] = val; - ret = size; - break; - case TGU_CONDITION_SELECT: - tgu_drvdata->value_table->condition_select[calculate_array_location( - tgu_drvdata, tgu_attr->step_index, - tgu_attr->operation_index, - tgu_attr->reg_num)] = val; - ret = size; - break; - case TGU_TIMER: - tgu_drvdata->value_table->timer[calculate_array_location( - tgu_drvdata, tgu_attr->step_index, tgu_attr->operation_index, - tgu_attr->reg_num)] = val; - ret = size; - break; - case TGU_COUNTER: - tgu_drvdata->value_table->counter[calculate_array_location( - tgu_drvdata, tgu_attr->step_index, tgu_attr->operation_index, - tgu_attr->reg_num)] = val; - ret = size; - break; - default: - break; - } - return ret; -} - -static umode_t tgu_node_visible(struct kobject *kobject, - struct attribute *attr, - int n) -{ - struct device *dev = kobj_to_dev(kobject); - struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent); - int ret = SYSFS_GROUP_INVISIBLE; - - struct device_attribute *dev_attr = - container_of(attr, struct device_attribute, attr); - struct tgu_attribute *tgu_attr = - container_of(dev_attr, struct tgu_attribute, attr); - - if (tgu_attr->step_index < drvdata->max_step) { - switch (tgu_attr->operation_index) { - case TGU_PRIORITY0: - case TGU_PRIORITY1: - case TGU_PRIORITY2: - case TGU_PRIORITY3: - ret = (tgu_attr->reg_num < drvdata->max_reg) ? - attr->mode : - 0; - break; - case TGU_CONDITION_DECODE: - ret = (tgu_attr->reg_num < - drvdata->max_condition_decode) ? - attr->mode : - 0; - break; - case TGU_CONDITION_SELECT: - /* 'default' register is at the end of 'select' region */ - if (tgu_attr->reg_num == - drvdata->max_condition_select - 1) - attr->name = "default"; - ret = (tgu_attr->reg_num < - drvdata->max_condition_select) ? - attr->mode : - 0; - break; - case TGU_COUNTER: - if (drvdata->max_counter == 0) - ret = SYSFS_GROUP_INVISIBLE; - else - ret = (tgu_attr->reg_num < - drvdata->max_counter) ? - attr->mode : - 0; - break; - case TGU_TIMER: - if (drvdata->max_timer == 0) - ret = SYSFS_GROUP_INVISIBLE; - else - ret = (tgu_attr->reg_num < - drvdata->max_timer) ? - attr->mode : - 0; - break; - default: - break; - } - } - return ret; -} - -static ssize_t tgu_write_all_hw_regs(struct tgu_drvdata *drvdata) -{ - int i, j, k, ret; - - CS_UNLOCK(drvdata->base); - - for (i = 0; i < drvdata->max_step; i++) { - for (j = 0; j < MAX_PRIORITY; j++) { - for (k = 0; k < drvdata->max_reg; k++) { - - ret = check_array_location(drvdata, i, j, k); - if (ret == -EINVAL) - goto exit; - - tgu_writel(drvdata, - drvdata->value_table->priority - [calculate_array_location( - drvdata, i, j, k)], - PRIORITY_REG_STEP(i, j, k)); - } - } - } - - for (i = 0; i < drvdata->max_step; i++) { - for (j = 0; j < drvdata->max_condition_decode; j++) { - ret = check_array_location(drvdata, i, TGU_CONDITION_DECODE, j); - if (ret == -EINVAL) - goto exit; - - tgu_writel(drvdata, - drvdata->value_table->condition_decode - [calculate_array_location( - drvdata, i, - TGU_CONDITION_DECODE, j)], - CONDITION_DECODE_STEP(i, j)); - } - } - - for (i = 0; i < drvdata->max_step; i++) { - for (j = 0; j < drvdata->max_condition_select; j++) { - ret = check_array_location(drvdata, i, TGU_CONDITION_SELECT, j); - if (ret == -EINVAL) - goto exit; - - tgu_writel(drvdata, - drvdata->value_table->condition_select - [calculate_array_location(drvdata, i, - TGU_CONDITION_SELECT, j)], - CONDITION_SELECT_STEP(i, j)); - } - } - - for (i = 0; i < drvdata->max_step; i++) { - for (j = 0; j < drvdata->max_timer; j++) { - ret = check_array_location(drvdata, i, TGU_TIMER, j); - if (ret == -EINVAL) - goto exit; - - tgu_writel(drvdata, - drvdata->value_table->timer - [calculate_array_location(drvdata, i, - TGU_TIMER, j)], - TIMER_COMPARE_STEP(i, j)); - } - } - - for (i = 0; i < drvdata->max_step; i++) { - for (j = 0; j < drvdata->max_counter; j++) { - ret = check_array_location(drvdata, i, TGU_COUNTER, j); - if (ret == -EINVAL) - goto exit; - - tgu_writel(drvdata, - drvdata->value_table->counter - [calculate_array_location(drvdata, i, - TGU_COUNTER, j)], - COUNTER_COMPARE_STEP(i, j)); - } - } - /* Enable TGU to program the triggers */ - tgu_writel(drvdata, 1, TGU_CONTROL); -exit: - CS_LOCK(drvdata->base); - return ret >= 0 ? 0 : ret; -} - -static void tgu_set_reg_number(struct tgu_drvdata *drvdata) -{ - int num_sense_input; - int num_reg; - u32 devid; - - devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); - - num_sense_input = TGU_DEVID_SENSE_INPUT(devid); - if (((num_sense_input * NUMBER_BITS_EACH_SIGNAL) % LENGTH_REGISTER) == 0) - num_reg = (num_sense_input * NUMBER_BITS_EACH_SIGNAL) / LENGTH_REGISTER; - else - num_reg = ((num_sense_input * NUMBER_BITS_EACH_SIGNAL) / LENGTH_REGISTER) + 1; - drvdata->max_reg = num_reg; -} - -static void tgu_set_steps(struct tgu_drvdata *drvdata) -{ - int num_steps; - u32 devid; - - devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); - - num_steps = TGU_DEVID_STEPS(devid); - - drvdata->max_step = num_steps; -} - -static void tgu_set_conditions(struct tgu_drvdata *drvdata) -{ - int num_conditions; - u32 devid; - - devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); - - num_conditions = TGU_DEVID_CONDITIONS(devid); - drvdata->max_condition_decode = num_conditions; - /* select region has an additional 'default' register */ - drvdata->max_condition_select = num_conditions + 1; -} - -static void tgu_set_timer_counter(struct tgu_drvdata *drvdata) -{ - int num_timers, num_counters; - u32 devid2; - - devid2 = readl_relaxed(drvdata->base + CORESIGHT_DEVID2); - - if (TGU_DEVID2_TIMER0(devid2) && TGU_DEVID2_TIMER1(devid2)) - num_timers = 2; - else if (TGU_DEVID2_TIMER0(devid2) || TGU_DEVID2_TIMER1(devid2)) - num_timers = 1; - else - num_timers = 0; - - if (TGU_DEVID2_COUNTER0(devid2) && TGU_DEVID2_COUNTER1(devid2)) - num_counters = 2; - else if (TGU_DEVID2_COUNTER0(devid2) || TGU_DEVID2_COUNTER1(devid2)) - num_counters = 1; - else - num_counters = 0; - - drvdata->max_timer = num_timers; - drvdata->max_counter = num_counters; -} - -static int tgu_enable(struct coresight_device *csdev, enum cs_mode mode, - void *data) -{ - int ret = 0; - struct tgu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - guard(spinlock)(&drvdata->spinlock); - if (drvdata->enable) - return -EBUSY; - - ret = tgu_write_all_hw_regs(drvdata); - - if (ret == -EINVAL) - goto exit; - drvdata->enable = true; - -exit: - return ret; -} - -static int tgu_disable(struct coresight_device *csdev, void *data) -{ - struct tgu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - spin_lock(&drvdata->spinlock); - if (drvdata->enable) { - CS_UNLOCK(drvdata->base); - tgu_writel(drvdata, 0, TGU_CONTROL); - CS_LOCK(drvdata->base); - - drvdata->enable = false; - } - spin_unlock(&drvdata->spinlock); - return 0; -} - -static ssize_t enable_tgu_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - bool enabled; - struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - enabled = drvdata->enable; - spin_unlock(&drvdata->spinlock); - - return sysfs_emit(buf, "%d\n", enabled); -} - -/* enable_tgu_store - Configure Trace and Gating Unit (TGU) triggers. */ -static ssize_t enable_tgu_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t size) -{ - int ret = 0; - unsigned long val; - struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 0, &val); - if (ret) - return ret; - - if (val) { - ret = pm_runtime_resume_and_get(dev->parent); - if (ret) - return ret; - ret = tgu_enable(drvdata->csdev, CS_MODE_SYSFS, NULL); - if (ret) - pm_runtime_put(dev->parent); - } else { - ret = tgu_disable(drvdata->csdev, NULL); - pm_runtime_put(dev->parent); - } - - if (ret) - return ret; - return size; -} -static DEVICE_ATTR_RW(enable_tgu); - -/* reset_tgu_store - Reset Trace and Gating Unit (TGU) configuration. */ -static ssize_t reset_tgu_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t size) -{ - unsigned long value; - struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent); - int i, j, ret; - - if (kstrtoul(buf, 0, &value) || value == 0) - return -EINVAL; - - if (!drvdata->enable) { - ret = pm_runtime_get_sync(drvdata->dev); - if (ret < 0) { - pm_runtime_put(drvdata->dev); - return ret; - } - } - - spin_lock(&drvdata->spinlock); - CS_UNLOCK(drvdata->base); - - tgu_writel(drvdata, 0, TGU_CONTROL); - - if (drvdata->value_table->priority) - memset(drvdata->value_table->priority, 0, - MAX_PRIORITY * drvdata->max_step * - drvdata->max_reg * sizeof(unsigned int)); - - if (drvdata->value_table->condition_decode) - memset(drvdata->value_table->condition_decode, 0, - drvdata->max_condition_decode * drvdata->max_step * - sizeof(unsigned int)); - - /* Initialize all condition registers to NOT(value=0x1000000) */ - for (i = 0; i < drvdata->max_step; i++) { - for (j = 0; j < drvdata->max_condition_decode; j++) { - drvdata->value_table - ->condition_decode[calculate_array_location( - drvdata, i, TGU_CONDITION_DECODE, j)] = - 0x1000000; - } - } - - if (drvdata->value_table->condition_select) - memset(drvdata->value_table->condition_select, 0, - drvdata->max_condition_select * drvdata->max_step * - sizeof(unsigned int)); - - if (drvdata->value_table->timer) - memset(drvdata->value_table->timer, 0, - (drvdata->max_step) * - (drvdata->max_timer) * - sizeof(unsigned int)); - - if (drvdata->value_table->counter) - memset(drvdata->value_table->counter, 0, - (drvdata->max_step) * - (drvdata->max_counter) * - sizeof(unsigned int)); - - dev_dbg(dev, "Coresight-TGU reset complete\n"); - - CS_LOCK(drvdata->base); - - drvdata->enable = false; - spin_unlock(&drvdata->spinlock); - pm_runtime_put(drvdata->dev); - - return size; -} -static DEVICE_ATTR_WO(reset_tgu); - -static const struct coresight_ops_helper tgu_helper_ops = { - .enable = tgu_enable, - .disable = tgu_disable, -}; - -static const struct coresight_ops tgu_ops = { - .helper_ops = &tgu_helper_ops, -}; - -static struct attribute *tgu_common_attrs[] = { - &dev_attr_enable_tgu.attr, - &dev_attr_reset_tgu.attr, - NULL, -}; - -static const struct attribute_group tgu_common_grp = { - .attrs = tgu_common_attrs, - { NULL }, -}; - -static const struct attribute_group *tgu_attr_groups[] = { - &tgu_common_grp, - PRIORITY_ATTRIBUTE_GROUP_INIT(0, 0), - PRIORITY_ATTRIBUTE_GROUP_INIT(0, 1), - PRIORITY_ATTRIBUTE_GROUP_INIT(0, 2), - PRIORITY_ATTRIBUTE_GROUP_INIT(0, 3), - PRIORITY_ATTRIBUTE_GROUP_INIT(1, 0), - PRIORITY_ATTRIBUTE_GROUP_INIT(1, 1), - PRIORITY_ATTRIBUTE_GROUP_INIT(1, 2), - PRIORITY_ATTRIBUTE_GROUP_INIT(1, 3), - PRIORITY_ATTRIBUTE_GROUP_INIT(2, 0), - PRIORITY_ATTRIBUTE_GROUP_INIT(2, 1), - PRIORITY_ATTRIBUTE_GROUP_INIT(2, 2), - PRIORITY_ATTRIBUTE_GROUP_INIT(2, 3), - PRIORITY_ATTRIBUTE_GROUP_INIT(3, 0), - PRIORITY_ATTRIBUTE_GROUP_INIT(3, 1), - PRIORITY_ATTRIBUTE_GROUP_INIT(3, 2), - PRIORITY_ATTRIBUTE_GROUP_INIT(3, 3), - PRIORITY_ATTRIBUTE_GROUP_INIT(4, 0), - PRIORITY_ATTRIBUTE_GROUP_INIT(4, 1), - PRIORITY_ATTRIBUTE_GROUP_INIT(4, 2), - PRIORITY_ATTRIBUTE_GROUP_INIT(4, 3), - PRIORITY_ATTRIBUTE_GROUP_INIT(5, 0), - PRIORITY_ATTRIBUTE_GROUP_INIT(5, 1), - PRIORITY_ATTRIBUTE_GROUP_INIT(5, 2), - PRIORITY_ATTRIBUTE_GROUP_INIT(5, 3), - PRIORITY_ATTRIBUTE_GROUP_INIT(6, 0), - PRIORITY_ATTRIBUTE_GROUP_INIT(6, 1), - PRIORITY_ATTRIBUTE_GROUP_INIT(6, 2), - PRIORITY_ATTRIBUTE_GROUP_INIT(6, 3), - PRIORITY_ATTRIBUTE_GROUP_INIT(7, 0), - PRIORITY_ATTRIBUTE_GROUP_INIT(7, 1), - PRIORITY_ATTRIBUTE_GROUP_INIT(7, 2), - PRIORITY_ATTRIBUTE_GROUP_INIT(7, 3), - CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(0), - CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(1), - CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(2), - CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(3), - CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(4), - CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(5), - CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(6), - CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(7), - CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(0), - CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(1), - CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(2), - CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(3), - CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(4), - CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(5), - CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(6), - CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(7), - TIMER_ATTRIBUTE_GROUP_INIT(0), - TIMER_ATTRIBUTE_GROUP_INIT(1), - TIMER_ATTRIBUTE_GROUP_INIT(2), - TIMER_ATTRIBUTE_GROUP_INIT(3), - TIMER_ATTRIBUTE_GROUP_INIT(4), - TIMER_ATTRIBUTE_GROUP_INIT(5), - TIMER_ATTRIBUTE_GROUP_INIT(6), - TIMER_ATTRIBUTE_GROUP_INIT(7), - COUNTER_ATTRIBUTE_GROUP_INIT(0), - COUNTER_ATTRIBUTE_GROUP_INIT(1), - COUNTER_ATTRIBUTE_GROUP_INIT(2), - COUNTER_ATTRIBUTE_GROUP_INIT(3), - COUNTER_ATTRIBUTE_GROUP_INIT(4), - COUNTER_ATTRIBUTE_GROUP_INIT(5), - COUNTER_ATTRIBUTE_GROUP_INIT(6), - COUNTER_ATTRIBUTE_GROUP_INIT(7), - NULL, -}; - -static int tgu_probe(struct amba_device *adev, const struct amba_id *id) -{ - int ret = 0; - struct device *dev = &adev->dev; - struct coresight_desc desc = { 0 }; - struct coresight_platform_data *pdata; - struct tgu_drvdata *drvdata; - - desc.name = coresight_alloc_device_name(&tgu_devs, dev); - if (!desc.name) - return -ENOMEM; - - pdata = coresight_get_platform_data(dev); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - - adev->dev.platform_data = pdata; - - drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); - if (!drvdata) - return -ENOMEM; - - drvdata->dev = &adev->dev; - dev_set_drvdata(dev, drvdata); - - drvdata->base = devm_ioremap_resource(dev, &adev->res); - if (!drvdata->base) - return -ENOMEM; - - spin_lock_init(&drvdata->spinlock); - - tgu_set_reg_number(drvdata); - tgu_set_steps(drvdata); - tgu_set_conditions(drvdata); - tgu_set_timer_counter(drvdata); - - drvdata->value_table = - devm_kzalloc(dev, sizeof(*drvdata->value_table), GFP_KERNEL); - if (!drvdata->value_table) - return -ENOMEM; - - drvdata->value_table->priority = devm_kzalloc( - dev, - MAX_PRIORITY * drvdata->max_reg * drvdata->max_step * - sizeof(*(drvdata->value_table->priority)), - GFP_KERNEL); - - if (!drvdata->value_table->priority) - return -ENOMEM; - - drvdata->value_table->condition_decode = devm_kzalloc( - dev, - drvdata->max_condition_decode * drvdata->max_step * - sizeof(*(drvdata->value_table->condition_decode)), - GFP_KERNEL); - - if (!drvdata->value_table->condition_decode) - return -ENOMEM; - - drvdata->value_table->condition_select = devm_kzalloc( - dev, - drvdata->max_condition_select * drvdata->max_step * - sizeof(*(drvdata->value_table->condition_select)), - GFP_KERNEL); - - if (!drvdata->value_table->condition_select) - return -ENOMEM; - - drvdata->value_table->timer = devm_kzalloc( - dev, - drvdata->max_step * drvdata->max_timer * - sizeof(*(drvdata->value_table->timer)), - GFP_KERNEL); - - if (!drvdata->value_table->timer) - return -ENOMEM; - - drvdata->value_table->counter = devm_kzalloc( - dev, - drvdata->max_step * drvdata->max_counter * - sizeof(*(drvdata->value_table->counter)), - GFP_KERNEL); - - if (!drvdata->value_table->counter) - return -ENOMEM; - - drvdata->enable = false; - desc.type = CORESIGHT_DEV_TYPE_HELPER; - desc.pdata = adev->dev.platform_data; - desc.dev = &adev->dev; - desc.ops = &tgu_ops; - desc.groups = tgu_attr_groups; - - drvdata->csdev = coresight_register(&desc); - if (IS_ERR(drvdata->csdev)) { - ret = PTR_ERR(drvdata->csdev); - goto err; - } - - pm_runtime_put(&adev->dev); - return 0; -err: - pm_runtime_put(&adev->dev); - return ret; -} - -static void tgu_remove(struct amba_device *adev) -{ - struct tgu_drvdata *drvdata = dev_get_drvdata(&adev->dev); - - coresight_unregister(drvdata->csdev); -} - -static const struct amba_id tgu_ids[] = { - { - .id = 0x000f0e00, - .mask = 0x000fffff, - .data = "TGU", - }, - { 0, 0, NULL }, -}; - -MODULE_DEVICE_TABLE(amba, tgu_ids); - -static struct amba_driver tgu_driver = { - .drv = { - .name = "coresight-tgu", - .suppress_bind_attrs = true, - }, - .probe = tgu_probe, - .remove = tgu_remove, - .id_table = tgu_ids, -}; - -module_amba_driver(tgu_driver); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("CoreSight TGU driver"); diff --git a/drivers/hwtracing/qcom/Kconfig b/drivers/hwtracing/qcom/Kconfig new file mode 100644 index 0000000000000..5c94c75ffa396 --- /dev/null +++ b/drivers/hwtracing/qcom/Kconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# QCOM specific hwtracing drivers +# +menu "Qualcomm specific hwtracing drivers" + +config QCOM_TGU + tristate "QCOM Trigger Generation Unit driver" + depends on ARCH_QCOM || COMPILE_TEST + depends on ARM_AMBA + help + This driver provides support for Trigger Generation Unit that is + used to detect patterns or sequences on a given set of signals. + TGU is used to monitor a particular bus within a given region to + detect illegal transaction sequences or slave responses. It is also + used to monitor a data stream to detect protocol violations and to + provide a trigger point for centering data around a specific event + within the trace data buffer. + +endmenu diff --git a/drivers/hwtracing/qcom/Makefile b/drivers/hwtracing/qcom/Makefile new file mode 100644 index 0000000000000..5a0a868c1ea0d --- /dev/null +++ b/drivers/hwtracing/qcom/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_QCOM_TGU) += tgu.o diff --git a/drivers/hwtracing/qcom/tgu.c b/drivers/hwtracing/qcom/tgu.c new file mode 100644 index 0000000000000..9fb51f2a912f9 --- /dev/null +++ b/drivers/hwtracing/qcom/tgu.c @@ -0,0 +1,704 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tgu.h" + +static int calculate_array_location(struct tgu_drvdata *drvdata, + int step_index, int operation_index, + int reg_index) +{ + switch (operation_index) { + case TGU_PRIORITY0: + case TGU_PRIORITY1: + case TGU_PRIORITY2: + case TGU_PRIORITY3: + return operation_index * (drvdata->num_step) * + (drvdata->num_reg) + + step_index * (drvdata->num_reg) + reg_index; + case TGU_CONDITION_DECODE: + return step_index * (drvdata->num_condition_decode) + + reg_index; + case TGU_CONDITION_SELECT: + return step_index * (drvdata->num_condition_select) + + reg_index; + case TGU_COUNTER: + return step_index * (drvdata->num_counter) + reg_index; + case TGU_TIMER: + return step_index * (drvdata->num_timer) + reg_index; + default: + break; + } + + return -EINVAL; +} + +static int check_array_location(struct tgu_drvdata *drvdata, int step, + int ops, int reg) +{ + int result = calculate_array_location(drvdata, step, ops, reg); + + if (result == -EINVAL) + dev_err(drvdata->dev, "check array location - Fail\n"); + + return result; +} + +static ssize_t tgu_dataset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tgu_drvdata *drvdata = dev_get_drvdata(dev); + struct tgu_attribute *tgu_attr = + container_of(attr, struct tgu_attribute, attr); + int index; + + index = check_array_location(drvdata, tgu_attr->step_index, + tgu_attr->operation_index, tgu_attr->reg_num); + + if (index == -EINVAL) + return index; + + switch (tgu_attr->operation_index) { + case TGU_PRIORITY0: + case TGU_PRIORITY1: + case TGU_PRIORITY2: + case TGU_PRIORITY3: + return sysfs_emit(buf, "0x%x\n", + drvdata->value_table->priority[index]); + case TGU_CONDITION_DECODE: + return sysfs_emit(buf, "0x%x\n", + drvdata->value_table->condition_decode[index]); + case TGU_CONDITION_SELECT: + return sysfs_emit(buf, "0x%x\n", + drvdata->value_table->condition_select[index]); + case TGU_TIMER: + return sysfs_emit(buf, "0x%x\n", + drvdata->value_table->timer[index]); + case TGU_COUNTER: + return sysfs_emit(buf, "0x%x\n", + drvdata->value_table->counter[index]); + default: + break; + } + return -EINVAL; +} + +static ssize_t tgu_dataset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct tgu_drvdata *tgu_drvdata = dev_get_drvdata(dev); + struct tgu_attribute *tgu_attr = + container_of(attr, struct tgu_attribute, attr); + unsigned long val; + int index; + int ret; + + ret = kstrtoul(buf, 0, &val); + if (ret) + return ret; + + guard(spinlock)(&tgu_drvdata->lock); + index = check_array_location(tgu_drvdata, tgu_attr->step_index, + tgu_attr->operation_index, + tgu_attr->reg_num); + + if (index == -EINVAL) + return index; + + switch (tgu_attr->operation_index) { + case TGU_PRIORITY0: + case TGU_PRIORITY1: + case TGU_PRIORITY2: + case TGU_PRIORITY3: + tgu_drvdata->value_table->priority[index] = val; + ret = size; + break; + case TGU_CONDITION_DECODE: + tgu_drvdata->value_table->condition_decode[index] = val; + ret = size; + break; + case TGU_CONDITION_SELECT: + tgu_drvdata->value_table->condition_select[index] = val; + ret = size; + break; + case TGU_TIMER: + tgu_drvdata->value_table->timer[index] = val; + ret = size; + break; + case TGU_COUNTER: + tgu_drvdata->value_table->counter[index] = val; + ret = size; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static umode_t tgu_node_visible(struct kobject *kobject, + struct attribute *attr, + int n) +{ + struct device *dev = kobj_to_dev(kobject); + struct tgu_drvdata *drvdata = dev_get_drvdata(dev); + struct device_attribute *dev_attr = + container_of(attr, struct device_attribute, attr); + struct tgu_attribute *tgu_attr = + container_of(dev_attr, struct tgu_attribute, attr); + + if (tgu_attr->step_index >= drvdata->num_step) + return SYSFS_GROUP_INVISIBLE; + + switch (tgu_attr->operation_index) { + case TGU_PRIORITY0: + case TGU_PRIORITY1: + case TGU_PRIORITY2: + case TGU_PRIORITY3: + if (tgu_attr->reg_num < drvdata->num_reg) + return attr->mode; + break; + case TGU_CONDITION_DECODE: + if (tgu_attr->reg_num < drvdata->num_condition_decode) + return attr->mode; + break; + case TGU_CONDITION_SELECT: + /* 'default' register is at the end of 'select' region */ + if (tgu_attr->reg_num == drvdata->num_condition_select - 1) + attr->name = "default"; + if (tgu_attr->reg_num < drvdata->num_condition_select) + return attr->mode; + break; + case TGU_COUNTER: + if (!drvdata->num_counter) + break; + if (tgu_attr->reg_num < drvdata->num_counter) + return attr->mode; + break; + case TGU_TIMER: + if (!drvdata->num_timer) + break; + if (tgu_attr->reg_num < drvdata->num_timer) + return attr->mode; + break; + default: + break; + } + + return 0; +} + +static ssize_t tgu_write_all_hw_regs(struct tgu_drvdata *drvdata) +{ + int i, j, k, index; + + TGU_UNLOCK(drvdata->base); + for (i = 0; i < drvdata->num_step; i++) { + for (j = 0; j < MAX_PRIORITY; j++) { + for (k = 0; k < drvdata->num_reg; k++) { + index = check_array_location( + drvdata, i, j, k); + if (index == -EINVAL) + goto exit; + + writel(drvdata->value_table->priority[index], + drvdata->base + + PRIORITY_REG_STEP(i, j, k)); + } + } + } + + for (i = 0; i < drvdata->num_step; i++) { + for (j = 0; j < drvdata->num_condition_decode; j++) { + index = check_array_location(drvdata, i, + TGU_CONDITION_DECODE, j); + if (index == -EINVAL) + goto exit; + + writel(drvdata->value_table->condition_decode[index], + drvdata->base + CONDITION_DECODE_STEP(i, j)); + } + } + + for (i = 0; i < drvdata->num_step; i++) { + for (j = 0; j < drvdata->num_condition_select; j++) { + index = check_array_location(drvdata, i, + TGU_CONDITION_SELECT, j); + if (index == -EINVAL) + goto exit; + + writel(drvdata->value_table->condition_select[index], + drvdata->base + CONDITION_SELECT_STEP(i, j)); + } + } + + for (i = 0; i < drvdata->num_step; i++) { + for (j = 0; j < drvdata->num_timer; j++) { + index = check_array_location(drvdata, i, TGU_TIMER, j); + + if (index == -EINVAL) + goto exit; + + writel(drvdata->value_table->timer[index], + drvdata->base + TIMER_COMPARE_STEP(i, j)); + } + } + + for (i = 0; i < drvdata->num_step; i++) { + for (j = 0; j < drvdata->num_counter; j++) { + index = check_array_location(drvdata, i, TGU_COUNTER, j); + + if (index == -EINVAL) + goto exit; + + writel(drvdata->value_table->counter[index], + drvdata->base + COUNTER_COMPARE_STEP(i, j)); + } + } + /* Enable TGU to program the triggers */ + writel(1, drvdata->base + TGU_CONTROL); +exit: + TGU_LOCK(drvdata->base); + return index >= 0 ? 0 : -EINVAL; +} + +static void tgu_set_reg_number(struct tgu_drvdata *drvdata) +{ + int num_sense_input; + int num_reg; + u32 devid; + + devid = readl(drvdata->base + TGU_DEVID); + + num_sense_input = TGU_DEVID_SENSE_INPUT(devid); + num_reg = (num_sense_input * TGU_BITS_PER_SIGNAL) / LENGTH_REGISTER; + + if ((num_sense_input * TGU_BITS_PER_SIGNAL) % LENGTH_REGISTER) + num_reg++; + + drvdata->num_reg = num_reg; +} + +static void tgu_set_steps(struct tgu_drvdata *drvdata) +{ + u32 devid; + + devid = readl(drvdata->base + TGU_DEVID); + + drvdata->num_step = TGU_DEVID_STEPS(devid); +} + +static void tgu_set_conditions(struct tgu_drvdata *drvdata) +{ + u32 devid; + + devid = readl(drvdata->base + TGU_DEVID); + drvdata->num_condition_decode = TGU_DEVID_CONDITIONS(devid); + /* select region has an additional 'default' register */ + drvdata->num_condition_select = TGU_DEVID_CONDITIONS(devid) + 1; +} + +static void tgu_set_timer_counter(struct tgu_drvdata *drvdata) +{ + int num_timers = 0, num_counters = 0; + u32 devid2; + + devid2 = readl(drvdata->base + CORESIGHT_DEVID2); + + if (TGU_DEVID2_TIMER0(devid2)) + num_timers++; + if (TGU_DEVID2_TIMER1(devid2)) + num_timers++; + + if (TGU_DEVID2_COUNTER0(devid2)) + num_counters++; + if (TGU_DEVID2_COUNTER1(devid2)) + num_counters++; + + drvdata->num_timer = num_timers; + drvdata->num_counter = num_counters; +} + +static int tgu_enable(struct device *dev) +{ + struct tgu_drvdata *drvdata = dev_get_drvdata(dev); + int ret; + + guard(spinlock)(&drvdata->lock); + + ret = tgu_write_all_hw_regs(drvdata); + if (!ret) + drvdata->enabled = true; + + return ret; +} + +static void tgu_do_disable(struct tgu_drvdata *drvdata) +{ + TGU_UNLOCK(drvdata->base); + writel(0, drvdata->base + TGU_CONTROL); + TGU_LOCK(drvdata->base); + + drvdata->enabled = false; +} + +static void tgu_disable(struct device *dev) +{ + struct tgu_drvdata *drvdata = dev_get_drvdata(dev); + + guard(spinlock)(&drvdata->lock); + if (!drvdata->enabled) + return; + + tgu_do_disable(drvdata); +} + +static ssize_t enable_tgu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tgu_drvdata *drvdata = dev_get_drvdata(dev); + bool enabled; + + guard(spinlock)(&drvdata->lock); + enabled = drvdata->enabled; + + return sysfs_emit(buf, "%d\n", !!enabled); +} + +/* enable_tgu_store - Configure Trace and Gating Unit (TGU) triggers. */ +static ssize_t enable_tgu_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + struct tgu_drvdata *drvdata = dev_get_drvdata(dev); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 0, &val); + if (ret || val > 1) + return -EINVAL; + + if (val) { + scoped_guard(spinlock, &drvdata->lock) { + if (drvdata->enabled) + return -EBUSY; + } + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = tgu_enable(dev); + if (ret) { + pm_runtime_put(dev); + return ret; + } + } else { + scoped_guard(spinlock, &drvdata->lock) { + if (!drvdata->enabled) + return -EINVAL; + } + + tgu_disable(dev); + pm_runtime_put(dev); + } + + return size; +} +static DEVICE_ATTR_RW(enable_tgu); + +/* reset_tgu_store - Reset Trace and Gating Unit (TGU) configuration. */ +static ssize_t reset_tgu_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct tgu_drvdata *drvdata = dev_get_drvdata(dev); + struct value_table *vt = drvdata->value_table; + u32 *cond_decode = drvdata->value_table->condition_decode; + unsigned long value; + int i, j, ret; + + if (kstrtoul(buf, 0, &value) || value != 1) + return -EINVAL; + + spin_lock(&drvdata->lock); + if (!drvdata->enabled) { + spin_unlock(&drvdata->lock); + ret = pm_runtime_resume_and_get(drvdata->dev); + if (ret) + return ret; + spin_lock(&drvdata->lock); + } + + tgu_do_disable(drvdata); + + if (vt->priority) { + size_t size = MAX_PRIORITY * drvdata->num_step * + drvdata->num_reg * sizeof(unsigned int); + memset(vt->priority, 0, size); + } + + if (vt->condition_decode) { + size_t size = drvdata->num_condition_decode * + drvdata->num_step * sizeof(unsigned int); + memset(vt->condition_decode, 0, size); + } + + /* Initialize all condition registers to NOT(value=0x1000000) */ + for (i = 0; i < drvdata->num_step; i++) { + for (j = 0; j < drvdata->num_condition_decode; j++) { + cond_decode[calculate_array_location(drvdata, i, + TGU_CONDITION_DECODE, j)] = 0x1000000; + } + } + + if (vt->condition_select) { + size_t size = drvdata->num_condition_select * + drvdata->num_step * sizeof(unsigned int); + memset(vt->condition_select, 0, size); + } + + if (vt->timer) { + size_t size = (drvdata->num_step) * (drvdata->num_timer) * + sizeof(unsigned int); + memset(vt->timer, 0, size); + } + + if (vt->counter) { + size_t size = (drvdata->num_step) * (drvdata->num_counter) * + sizeof(unsigned int); + memset(vt->counter, 0, size); + } + + spin_unlock(&drvdata->lock); + + dev_dbg(dev, "Qualcomm-TGU reset complete\n"); + + pm_runtime_put(drvdata->dev); + + return size; +} +static DEVICE_ATTR_WO(reset_tgu); + +static struct attribute *tgu_common_attrs[] = { + &dev_attr_enable_tgu.attr, + &dev_attr_reset_tgu.attr, + NULL, +}; + +static const struct attribute_group tgu_common_grp = { + .attrs = tgu_common_attrs, + NULL, +}; + +static const struct attribute_group *tgu_attr_groups[] = { + &tgu_common_grp, + PRIORITY_ATTRIBUTE_GROUP_INIT(0, 0), + PRIORITY_ATTRIBUTE_GROUP_INIT(0, 1), + PRIORITY_ATTRIBUTE_GROUP_INIT(0, 2), + PRIORITY_ATTRIBUTE_GROUP_INIT(0, 3), + PRIORITY_ATTRIBUTE_GROUP_INIT(1, 0), + PRIORITY_ATTRIBUTE_GROUP_INIT(1, 1), + PRIORITY_ATTRIBUTE_GROUP_INIT(1, 2), + PRIORITY_ATTRIBUTE_GROUP_INIT(1, 3), + PRIORITY_ATTRIBUTE_GROUP_INIT(2, 0), + PRIORITY_ATTRIBUTE_GROUP_INIT(2, 1), + PRIORITY_ATTRIBUTE_GROUP_INIT(2, 2), + PRIORITY_ATTRIBUTE_GROUP_INIT(2, 3), + PRIORITY_ATTRIBUTE_GROUP_INIT(3, 0), + PRIORITY_ATTRIBUTE_GROUP_INIT(3, 1), + PRIORITY_ATTRIBUTE_GROUP_INIT(3, 2), + PRIORITY_ATTRIBUTE_GROUP_INIT(3, 3), + PRIORITY_ATTRIBUTE_GROUP_INIT(4, 0), + PRIORITY_ATTRIBUTE_GROUP_INIT(4, 1), + PRIORITY_ATTRIBUTE_GROUP_INIT(4, 2), + PRIORITY_ATTRIBUTE_GROUP_INIT(4, 3), + PRIORITY_ATTRIBUTE_GROUP_INIT(5, 0), + PRIORITY_ATTRIBUTE_GROUP_INIT(5, 1), + PRIORITY_ATTRIBUTE_GROUP_INIT(5, 2), + PRIORITY_ATTRIBUTE_GROUP_INIT(5, 3), + PRIORITY_ATTRIBUTE_GROUP_INIT(6, 0), + PRIORITY_ATTRIBUTE_GROUP_INIT(6, 1), + PRIORITY_ATTRIBUTE_GROUP_INIT(6, 2), + PRIORITY_ATTRIBUTE_GROUP_INIT(6, 3), + PRIORITY_ATTRIBUTE_GROUP_INIT(7, 0), + PRIORITY_ATTRIBUTE_GROUP_INIT(7, 1), + PRIORITY_ATTRIBUTE_GROUP_INIT(7, 2), + PRIORITY_ATTRIBUTE_GROUP_INIT(7, 3), + CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(0), + CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(1), + CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(2), + CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(3), + CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(4), + CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(5), + CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(6), + CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(7), + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(0), + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(1), + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(2), + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(3), + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(4), + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(5), + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(6), + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(7), + TIMER_ATTRIBUTE_GROUP_INIT(0), + TIMER_ATTRIBUTE_GROUP_INIT(1), + TIMER_ATTRIBUTE_GROUP_INIT(2), + TIMER_ATTRIBUTE_GROUP_INIT(3), + TIMER_ATTRIBUTE_GROUP_INIT(4), + TIMER_ATTRIBUTE_GROUP_INIT(5), + TIMER_ATTRIBUTE_GROUP_INIT(6), + TIMER_ATTRIBUTE_GROUP_INIT(7), + COUNTER_ATTRIBUTE_GROUP_INIT(0), + COUNTER_ATTRIBUTE_GROUP_INIT(1), + COUNTER_ATTRIBUTE_GROUP_INIT(2), + COUNTER_ATTRIBUTE_GROUP_INIT(3), + COUNTER_ATTRIBUTE_GROUP_INIT(4), + COUNTER_ATTRIBUTE_GROUP_INIT(5), + COUNTER_ATTRIBUTE_GROUP_INIT(6), + COUNTER_ATTRIBUTE_GROUP_INIT(7), + NULL, +}; + +static int tgu_probe(struct amba_device *adev, const struct amba_id *id) +{ + struct device *dev = &adev->dev; + struct tgu_drvdata *drvdata; + unsigned int *priority, *condition, *select, *timer, *counter; + size_t priority_size, condition_size, select_size, timer_size, counter_size; + int ret; + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->dev = &adev->dev; + dev_set_drvdata(dev, drvdata); + + drvdata->base = devm_ioremap_resource(dev, &adev->res); + if (IS_ERR(drvdata->base)) + return PTR_ERR(drvdata->base); + + spin_lock_init(&drvdata->lock); + + tgu_set_reg_number(drvdata); + tgu_set_steps(drvdata); + tgu_set_conditions(drvdata); + tgu_set_timer_counter(drvdata); + + ret = sysfs_create_groups(&dev->kobj, tgu_attr_groups); + if (ret) { + dev_err(dev, "failed to create sysfs groups: %d\n", ret); + return ret; + } + + drvdata->value_table = + devm_kzalloc(dev, sizeof(*drvdata->value_table), GFP_KERNEL); + if (!drvdata->value_table) + return -ENOMEM; + + priority_size = MAX_PRIORITY * drvdata->num_reg * drvdata->num_step; + + priority = devm_kcalloc(dev, priority_size, + sizeof(*drvdata->value_table->priority), + GFP_KERNEL); + if (!priority) + return -ENOMEM; + + drvdata->value_table->priority = priority; + + condition_size = drvdata->num_condition_decode * drvdata->num_step; + + condition = devm_kcalloc(dev, condition_size, + sizeof(*(drvdata->value_table->condition_decode)), + GFP_KERNEL); + if (!condition) + return -ENOMEM; + + drvdata->value_table->condition_decode = condition; + + select_size = drvdata->num_condition_select * drvdata->num_step; + + select = devm_kcalloc(dev, select_size, + sizeof(*(drvdata->value_table->condition_select)), + GFP_KERNEL); + if (!select) + return -ENOMEM; + + drvdata->value_table->condition_select = select; + + timer_size = drvdata->num_step * drvdata->num_timer; + + timer = devm_kcalloc(dev, timer_size, + sizeof(*(drvdata->value_table->timer)), + GFP_KERNEL); + if (!timer) + return -ENOMEM; + + drvdata->value_table->timer = timer; + + counter_size = drvdata->num_step * drvdata->num_counter; + + counter = devm_kcalloc(dev, counter_size, + sizeof(*(drvdata->value_table->counter)), + GFP_KERNEL); + if (!counter) + return -ENOMEM; + + drvdata->value_table->counter = counter; + + drvdata->enabled = false; + + pm_runtime_put(&adev->dev); + + return 0; +} + +static void tgu_remove(struct amba_device *adev) +{ + struct device *dev = &adev->dev; + + sysfs_remove_groups(&dev->kobj, tgu_attr_groups); + + tgu_disable(dev); +} + +static const struct amba_id tgu_ids[] = { + { + .id = 0x000f0e00, + .mask = 0x000fffff, + }, + { 0, 0, NULL }, +}; + +MODULE_DEVICE_TABLE(amba, tgu_ids); + +static struct amba_driver tgu_driver = { + .drv = { + .name = "qcom-tgu", + .suppress_bind_attrs = true, + }, + .probe = tgu_probe, + .remove = tgu_remove, + .id_table = tgu_ids, +}; + +module_amba_driver(tgu_driver); + +MODULE_AUTHOR("Songwei Chai "); +MODULE_AUTHOR("Jinlong Mao "); +MODULE_DESCRIPTION("Qualcomm Trigger Generation Unit driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwtracing/coresight/coresight-tgu.h b/drivers/hwtracing/qcom/tgu.h similarity index 74% rename from drivers/hwtracing/coresight/coresight-tgu.h rename to drivers/hwtracing/qcom/tgu.h index be9c87ec7e3ce..1bcbc99169def 100644 --- a/drivers/hwtracing/coresight/coresight-tgu.h +++ b/drivers/hwtracing/qcom/tgu.h @@ -1,27 +1,35 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ -#ifndef _CORESIGHT_TGU_H -#define _CORESIGHT_TGU_H +#ifndef _QCOM_TGU_H +#define _QCOM_TGU_H /* Register addresses */ -#define TGU_CONTROL 0x0000 +#define TGU_CONTROL 0x0000 +#define TGU_LAR 0xfb0 +#define TGU_UNLOCK_OFFSET 0xc5acce55 +#define TGU_DEVID 0xfc8 #define CORESIGHT_DEVID2 0xfc0 -/* Register read/write */ -#define tgu_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off) -#define tgu_readl(drvdata, off) __raw_readl(drvdata->base + off) - -#define TGU_DEVID_SENSE_INPUT(devid_val) ((int) BMVAL(devid_val, 10, 17)) -#define TGU_DEVID_STEPS(devid_val) ((int)BMVAL(devid_val, 3, 6)) -#define TGU_DEVID_CONDITIONS(devid_val) ((int)BMVAL(devid_val, 0, 2)) -#define TGU_DEVID2_TIMER0(devid_val) ((int)BMVAL(devid_val, 18, 23)) -#define TGU_DEVID2_TIMER1(devid_val) ((int)BMVAL(devid_val, 13, 17)) -#define TGU_DEVID2_COUNTER0(devid_val) ((int)BMVAL(devid_val, 6, 11)) -#define TGU_DEVID2_COUNTER1(devid_val) ((int)BMVAL(devid_val, 0, 5)) - -#define NUMBER_BITS_EACH_SIGNAL 4 + +#define TGU_DEVID_SENSE_INPUT(devid_val) \ + ((int)FIELD_GET(GENMASK(17, 10), devid_val)) +#define TGU_DEVID_STEPS(devid_val) \ + ((int)FIELD_GET(GENMASK(6, 3), devid_val)) +#define TGU_DEVID_CONDITIONS(devid_val) \ + ((int)FIELD_GET(GENMASK(2, 0), devid_val)) +#define TGU_DEVID2_TIMER0(devid_val) \ + ((int)FIELD_GET(GENMASK(23, 18), devid_val)) +#define TGU_DEVID2_TIMER1(devid_val) \ + ((int)FIELD_GET(GENMASK(17, 13), devid_val)) +#define TGU_DEVID2_COUNTER0(devid_val) \ + ((int)FIELD_GET(GENMASK(11, 6), devid_val)) +#define TGU_DEVID2_COUNTER1(devid_val) \ + ((int)FIELD_GET(GENMASK(5, 0), devid_val)) + + +#define TGU_BITS_PER_SIGNAL 4 #define LENGTH_REGISTER 32 /* @@ -32,13 +40,13 @@ * | | | reserve |$ * | | | |$ * |coresight management | |-------------|base+n*0x1D8+0x1F4$ - * | registe | |---> |prioroty[3] |$ + * | registers | |---> |priority[3] |$ * | | | |-------------|base+n*0x1D8+0x194$ - * | | | |prioroty[2] |$ + * | | | |priority[2] |$ * |-------------------------| | |-------------|base+n*0x1D8+0x134$ - * | | | |prioroty[1] |$ + * | | | |priority[1] |$ * | step[7] | | |-------------|base+n*0x1D8+0xD4$ - * |-------------------------|->base+0x40+7*0x1D8 | |prioroty[0] |$ + * |-------------------------|->base+0x40+7*0x1D8 | |priority[0] |$ * | | | |-------------|base+n*0x1D8+0x74$ * | ... | | | condition |$ * | | | | select |$ @@ -64,20 +72,20 @@ /* Calculate compare step addresses */ #define PRIORITY_REG_STEP(step, priority, reg)\ (PRIORITY_START_OFFSET + PRIORITY_OFFSET * priority +\ - REG_OFFSET * reg + STEP_OFFSET * step) + REG_OFFSET * reg + STEP_OFFSET * step) #define CONDITION_DECODE_STEP(step, decode) \ (CONDITION_DECODE_OFFSET + REG_OFFSET * decode + STEP_OFFSET * step) +#define CONDITION_SELECT_STEP(step, select) \ + (CONDITION_SELECT_OFFSET + REG_OFFSET * select + STEP_OFFSET * step) + #define TIMER_COMPARE_STEP(step, timer) \ (TIMER_START_OFFSET + REG_OFFSET * timer + STEP_OFFSET * step) #define COUNTER_COMPARE_STEP(step, counter) \ (COUNTER_START_OFFSET + REG_OFFSET * counter + STEP_OFFSET * step) -#define CONDITION_SELECT_STEP(step, select) \ - (CONDITION_SELECT_OFFSET + REG_OFFSET * select + STEP_OFFSET * step) - #define tgu_dataset_rw(name, step_index, type, reg_num) \ (&((struct tgu_attribute[]){ { \ __ATTR(name, 0644, tgu_dataset_show, tgu_dataset_store), \ @@ -88,24 +96,20 @@ #define STEP_PRIORITY(step_index, reg_num, priority) \ tgu_dataset_rw(reg##reg_num, step_index, TGU_PRIORITY##priority, \ - reg_num) - + reg_num) #define STEP_DECODE(step_index, reg_num) \ tgu_dataset_rw(reg##reg_num, step_index, TGU_CONDITION_DECODE, reg_num) - #define STEP_SELECT(step_index, reg_num) \ tgu_dataset_rw(reg##reg_num, step_index, TGU_CONDITION_SELECT, reg_num) - #define STEP_TIMER(step_index, reg_num) \ tgu_dataset_rw(reg##reg_num, step_index, TGU_TIMER, reg_num) - #define STEP_COUNTER(step_index, reg_num) \ tgu_dataset_rw(reg##reg_num, step_index, TGU_COUNTER, reg_num) -#define STEP_PRIORITY_LIST(step_index, priority) \ - {STEP_PRIORITY(step_index, 0, priority), \ +#define STEP_PRIORITY_LIST(step_index, priority) \ + {STEP_PRIORITY(step_index, 0, priority), \ STEP_PRIORITY(step_index, 1, priority), \ - STEP_PRIORITY(step_index, 2, priority), \ + STEP_PRIORITY(step_index, 2, priority), \ STEP_PRIORITY(step_index, 3, priority), \ STEP_PRIORITY(step_index, 4, priority), \ STEP_PRIORITY(step_index, 5, priority), \ @@ -121,7 +125,7 @@ STEP_PRIORITY(step_index, 15, priority), \ STEP_PRIORITY(step_index, 16, priority), \ STEP_PRIORITY(step_index, 17, priority), \ - NULL \ + NULL \ } #define STEP_DECODE_LIST(n) \ @@ -129,7 +133,7 @@ STEP_DECODE(n, 1), \ STEP_DECODE(n, 2), \ STEP_DECODE(n, 3), \ - NULL \ + NULL \ } #define STEP_SELECT_LIST(n) \ @@ -138,19 +142,19 @@ STEP_SELECT(n, 2), \ STEP_SELECT(n, 3), \ STEP_SELECT(n, 4), \ - NULL \ + NULL \ } #define STEP_TIMER_LIST(n) \ {STEP_TIMER(n, 0), \ STEP_TIMER(n, 1), \ - NULL \ + NULL \ } #define STEP_COUNTER_LIST(n) \ {STEP_COUNTER(n, 0), \ STEP_COUNTER(n, 1), \ - NULL \ + NULL \ } #define PRIORITY_ATTRIBUTE_GROUP_INIT(step, priority)\ @@ -196,7 +200,7 @@ enum operation_index { TGU_CONDITION_DECODE, TGU_CONDITION_SELECT, TGU_TIMER, - TGU_COUNTER + TGU_COUNTER, }; /* Maximum priority that TGU supports */ @@ -217,20 +221,37 @@ struct value_table { unsigned int *counter; }; +static inline void TGU_LOCK(void __iomem *addr) +{ + do { + /* Wait for things to settle */ + mb(); + writel_relaxed(0x0, addr + TGU_LAR); + } while (0); +} + +static inline void TGU_UNLOCK(void __iomem *addr) +{ + do { + writel_relaxed(TGU_UNLOCK_OFFSET, addr + TGU_LAR); + /* Make sure everyone has seen this */ + mb(); + } while (0); +} + /** * struct tgu_drvdata - Data structure for a TGU (Trigger Generator Unit) * @base: Memory-mapped base address of the TGU device * @dev: Pointer to the associated device structure - * @csdev: Pointer to the associated coresight device - * @spinlock: Spinlock for handling concurrent access - * @enable: Flag indicating whether the TGU device is enabled - * @value_table: Store given value based on relevant parameters. - * @max_reg: Maximum number of registers - * @max_step: Maximum step size - * @max_condition_decode: Maximum number of condition_decode - * @max_condition_select: Maximum number of condition_select - * @max_timer: Maximum number of timers - * @max_counter: Maximum number of counters + * @lock: Spinlock for handling concurrent access to private data + * @enabled: Flag indicating whether the TGU device is enabled + * @value_table: Store given value based on relevant parameters + * @num_reg: Maximum number of registers + * @num_step: Maximum step size + * @num_condition_decode: Maximum number of condition_decode + * @num_condition_select: Maximum number of condition_select + * @num_timer: Maximum number of timers + * @num_counter: Maximum number of counters * * This structure defines the data associated with a TGU device, * including its base address, device pointers, clock, spinlock for @@ -240,16 +261,15 @@ struct value_table { struct tgu_drvdata { void __iomem *base; struct device *dev; - struct coresight_device *csdev; - spinlock_t spinlock; - bool enable; + spinlock_t lock; + bool enabled; struct value_table *value_table; - int max_reg; - int max_step; - int max_condition_decode; - int max_condition_select; - int max_timer; - int max_counter; + int num_reg; + int num_step; + int num_condition_decode; + int num_condition_select; + int num_timer; + int num_counter; }; #endif