* [PATCH v15 0/7] Provide support for Trigger Generation Unit
From: Songwei Chai @ 2026-07-01 5:55 UTC (permalink / raw)
To: andersson, alexander.shishkin, mike.leach, konrad.dybcio,
suzuki.poulose, james.clark, krzk+dt, conor+dt
Cc: Songwei Chai, linux-kernel, linux-arm-kernel, linux-arm-msm,
coresight, devicetree, gregkh
We propose creating a new qcom directory under drivers/hwtracing
to host this TGU driver, as well as additional Qualcomm-specific
hwtracing drivers that we plan to submit in the coming months.
This structure will help organize vendor-specific implementations
and facilitate future development and maintenance.
Feedback from the community on this proposal is highly appreciated.
- Why we are proposing this:
TGU has the ability to monitor signal conditions and trigger debug-related
actions, serving as a programmable hardware component that enhances system
trace and debug capabilities. Placing it under drivers/hwtracing aligns
with its function as a trace generation utility.
We previously attempted to push this driver to drivers/hwtracing/coresight,
but did not receive support from the maintainers of the CoreSight
subsystem. The reason provided was: “This component is primarily a part
of the Qualcomm proprietary QPMDA subsystem, and is capable of operating
independently from the CoreSight hardware trace generation system.”
Chat history : https://lore.kernel.org/all/CAJ9a7ViKxHThyZfFFDV_FkNRimk4uo1NrMtQ-kcaj1qO4ZcGnA@mail.gmail.com/
Given this, we have been considering whether it would be appropriate
to create a dedicated drivers/hwtracing/qcom directory for
Qualcomm-related hwtracing drivers. This would follow the precedent set
by Intel, which maintains its own directory at drivers/hwtracing/intel_th.
We believe this structure would significantly facilitate
future submissions of related Qualcomm drivers.
- Maintenance of drivers/hwtracing/qcom:
Bjorn, who maintains linux-arm-msm, will be the maintainer of this
directory — we’ve discussed this with him and he’s aware that his task
list may grow accordingly. Additionally, Qualcomm engineers familiar with
the debug hardware — such as [Tingwei Zhang, Jinlong Mao, Songwei Chai],
will be available to review incoming patches and support ongoing
development.
- Detail for TGU:
This component can be utilized to sense a plurality of signals and
create a trigger into the CTI or generate interrupts to processors
once the input signal meets the conditions. We can treat the TGU’s
workflow as a flowsheet, it has some “steps” regions for customization.
In each step region, we can set the signals that we want with priority
in priority_group, set the conditions in each step via condition_decode,
and set the resultant action by condition_select. Meanwhile,
some TGUs (not all) also provide timer/counter functionality.
Based on the characteristics described above, we consider the TGU as a
helper in the CoreSight subsystem. Its master device is the TPDM, which
can transmit signals from other subsystems, and we reuse the existing
ports mechanism to link the TPDM to the connected TGU.
Here is a detailed example to explain how to use the TGU:
In this example, the TGU is configured to use 2 conditions, 2 steps, and
the timer. The goal is to look for one of two patterns which are generated
from TPDM, giving priority to one, and then generate a trigger once the
timer reaches a certain value. In other words, two conditions are used
for the first step to look for the two patterns, where the one with the
highest priority is used in the first condition. Then, in the second step,
the timer is enabled and set to be compared to the given value at each
clock cycle. These steps are better shown below.
|-----------------|
| |
| TPDM |
| |
|-----------------|
|
|
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ------
| | |
| | |--------------------| |
| |---- ---> | | Go to next steps | |
| | | |--- ---> | Enable timer | |
| | v | | | |
| | |-----------------| | |--------------------| |
| | | | Yes | | |
| | | inputs==0xB | ----->| | <-------- |
| | | | | | No | |
| No | |-----------------| | v | |
| | | | |-----------------| | |
| | | | | | | |
| | | | | timer>=3 |-- |
| | v | | | |
| | |-----------------| | |-----------------| |
| | | | Yes | | |
| |--- | inputs==0xA | ----->| | Yes |
| | | | |
| |-----------------| v |
| |-----------------| |
| | | |
| | Trigger | |
| | | |
| |-----------------| |
| TGU | |
|--- --- --- --- --- --- --- --- --- --- --- --- --- --- |--- --- -- |
|
v
|-----------------|
|The controllers |
|which will use |
|triggers further |
|-----------------|
steps:
1. Reset TGU /*it will disable tgu and reset dataset*/
- echo 1 > /sys/bus/amba/devices/<tgu-name>/reset_tgu
2. Set the pattern match for priority0 to 0xA = 0b1010 and for
priority 1 to 0xB = 0b1011.
- echo 0x11113232 > /sys/bus/amba/devices/<tgu-name>/step0_priority0/reg0
- echo 0x11113233 > /sys/bus/amba/devices/<tgu-name>/step0_priority1/reg0
Note:
Bit distribution diagram for each priority register
|-------------------------------------------------------------------|
| Bits | Field Nam | Description |
|-------------------------------------------------------------------|
| | | 00 = bypass for OR output |
| 29:28 | SEL_BIT7_TYPE2 | 01 = bypass for AND output |
| | | 10 = sense input '0' is true|
| | | 11 = sense input '1' is true|
|-------------------------------------------------------------------|
| | | 00 = bypass for OR output |
| 25:24 | SEL_BIT6_TYPE2 | 01 = bypass for AND output |
| | | 10 = sense input '0' is true|
| | | 11 = sense input '1' is true|
|-------------------------------------------------------------------|
| | | 00 = bypass for OR output |
| 21:20 | SEL_BIT5_TYPE2 | 01 = bypass for AND output |
| | | 10 = sense input '0' is true|
| | | 11 = sense input '1' is true|
|-------------------------------------------------------------------|
| | | 00 = bypass for OR output |
| 17:16 | SEL_BIT4_TYPE2 | 01 = bypass for AND output |
| | | 10 = sense input '0' is true|
| | | 11 = sense input '1' is true|
|-------------------------------------------------------------------|
| | | 00 = bypass for OR output |
| 13:12 | SEL_BIT3_TYPE2 | 01 = bypass for AND output |
| | | 10 = sense input '0' is true|
| | | 11 = sense input '1' is true|
|-------------------------------------------------------------------|
| | | 00 = bypass for OR output |
| 9:8 | SEL_BIT2_TYPE2 | 01 = bypass for AND output |
| | | 10 = sense input '0' is true|
| | | 11 = sense input '1' is true|
|-------------------------------------------------------------------|
| | | 00 = bypass for OR output |
| 5:4 | SEL_BIT1_TYPE2 | 01 = bypass for AND output |
| | | 10 = sense input '0' is true|
| | | 11 = sense input '1' is true|
|-------------------------------------------------------------------|
| | | 00 = bypass for OR output |
| 1:0 | SEL_BIT0_TYPE2 | 01 = bypass for AND output |
| | | 10 = sense input '0' is true|
| | | 11 = sense input '1' is true|
|-------------------------------------------------------------------|
These bits are used to identify the signals we want to sense, with
a maximum signal number of 140. For example, to sense the signal
0xA (binary 1010), we set the value of bits 0 to 13 to 3232, which
represents 1010. The remaining bits are set to 1, as we want to use
AND gate to summarize all the signals we want to sense here. For
rising or falling edge detection of any input to the priority, set
the remaining bits to 0 to use an OR gate.
3. look for the pattern for priority_i i=0,1.
- echo 0x3 > /sys/bus/amba/devices/<tgu-name>/step0_condition_decode/reg0
- echo 0x30 > /sys/bus/amba/devices/<tgu-name>/step0_condition_decode/reg1
|-------------------------------------------------------------------------------|
| Bits | Field Nam | Description |
|-------------------------------------------------------------------------------|
| | |For each decoded condition, this |
| 24 | NOT |inverts the output. If the condition |
| | |decodes to true, and the NOT field |
| | |is '1', then the output is NOT true. |
|-------------------------------------------------------------------------------|
| | |When '1' the output from the associated|
| 21 | BC0_COMP_ACTIVE |comparator will be actively included in|
| | |the decoding of this particular |
| | |condition. |
|-------------------------------------------------------------------------------|
| | |When '1' the output from the associated|
| | |comparator will need to be 1 to affect |
| 20 | BC0_COMP_HIGH |the decoding of this condition. |
| | |Conversely, a '0' here requires a '0' |
| | |from the comparator |
|-------------------------------------------------------------------------------|
| | |When '1' the output from the associated|
| 17 | |comparator will be actively included in|
| | TC0_COMP_ACTIVE |the decoding of this particular |
| | |condition. |
|-------------------------------------------------------------------------------|
| | |When '1' the output from the associated|
| | |comparator will need to be 1 to affect |
| 16 | TC0_COMP_HIGH |the decoding of this particular |
| | |condition.Conversely, a 0 here |
| | |requires a '0' from the comparator |
|-------------------------------------------------------------------------------|
| | |When '1' the output from Priority_n |
| | |OR logic will be actively |
| 4n+3 | Priority_n_OR_ACTIVE|included in the decoding of |
| | (n=0,1,2,3) |this particular condition. |
| | | |
|-------------------------------------------------------------------------------|
| | |When '1' the output from Priority_n |
| | |will need to be '1' to affect the |
| 4n+2 | Priority_n_OR_HIGH |decoding of this particular |
| | (n=0,1,2,3) |condition. Conversely, a '0' here |
| | |requires a '0' from Priority_n OR logic|
|-------------------------------------------------------------------------------|
| | |When '1' the output from Priority_n |
| | |AND logic will be actively |
| 4n+1 |Priority_n_AND_ACTIVE|included in the decoding of this |
| | (n=0,1,2,3) |particular condition. |
| | | |
|-------------------------------------------------------------------------------|
| | |When '1' the output from Priority_n |
| | |AND logic will need to be '1' to |
| 4n | Priority_n_AND_HIGH |affect the decoding of this |
| | (n=0,1,2,3) |particular condition. Conversely, |
| | |a '0' here requires a '0' from |
| | |Priority_n AND logic. |
|-------------------------------------------------------------------------------|
Since we use `priority_0` and `priority_1` with an AND output in step 2, we set `0x3`
and `0x30` here to activate them.
4. Set NEXT_STEP = 1 and TC0_ENABLE = 1 so that when the conditions
are met then the next step will be step 1 and the timer will be enabled.
- echo 0x20008 > /sys/bus/amba/devices/<tgu-name>/step0_condition_select/reg0
- echo 0x20008 > /sys/bus/amba/devices/<tgu-name>/step0_condition_select/reg1
|-----------------------------------------------------------------------------|
| Bits | Field Nam | Description |
|-----------------------------------------------------------------------------|
| | |This field defines the next step the |
| 18:17 | NEXT_STEP |TGU will 'goto' for the associated |
| | |Condition and Step. |
|-----------------------------------------------------------------------------|
| | |For each possible output trigger |
| 13 | TRIGGER |available, set a '1' if you want |
| | |the trigger to go active for the |
| | |associated condition and Step. |
|-----------------------------------------------------------------------------|
| | |This will cause BC0 to increment if the|
| 9 | BC0_INC |associated Condition is decoded for |
| | |this step. |
|-----------------------------------------------------------------------------|
| | |This will cause BC0 to decrement if the|
| 8 | BC0_DEC |associated Condition is decoded for |
| | |this step. |
|-----------------------------------------------------------------------------|
| | |This will clear BC0 count value to 0 if|
| 7 | BC0_CLEAR |the associated Condition is decoded |
| | |for this step. |
|-----------------------------------------------------------------------------|
| | |This will cause TC0 to increment until |
| 3 | TC0_ENABLE |paused or cleared if the associated |
| | |Condition is decoded for this step. |
|-----------------------------------------------------------------------------|
| | |This will cause TC0 to pause until |
| 2 | TC0_PAUSE |enabled if the associated Condition |
| | |is decoded for this step. |
|-----------------------------------------------------------------------------|
| | |This will clear TC0 count value to 0 |
| 1 | TC0_CLEAR |if the associated Condition is |
| | |decoded for this step. |
|-----------------------------------------------------------------------------|
| | |This will set the done signal to the |
| 0 | DONE |TGU FSM if the associated Condition |
| | |is decoded for this step. |
|-----------------------------------------------------------------------------|
Based on the distribution diagram, we set `0x20008` for `priority0` and `priority1` to
achieve "jump to step 1 and enable TC0" once the signal is sensed.
5. activate the timer comparison for this step.
- echo 0x30000 > /sys/bus/amba/devices/<tgu-name>/step1_condition_decode/reg0
|-------------------------------------------------------------------------------|
| | |When '1' the output from the associated|
| 17 | |comparator will be actively included in|
| | TC0_COMP_ACTIVE |the decoding of this particular |
| | |condition. |
|-------------------------------------------------------------------------------|
| | |When '1' the output from the associated|
| | |comparator will need to be 1 to affect |
| 16 | TC0_COMP_HIGH |the decoding of this particular |
| | |condition.Conversely, a 0 here |
| | |requires a '0' from the comparator |
|-------------------------------------------------------------------------------|
Accroding to the decode distribution diagram , we give 0x30000 here to set 16th&17th bit
to enable timer comparison.
6. Set the NEXT_STEP = 0 and TC0_PAUSE = 1 and TC0_CLEAR = 1 once the timer
has reached the given value.
- echo 0x6 > /sys/bus/amba/devices/<tgu-name>/step1_condition_select/reg0
7. Enable Trigger 0 for TGU when the condition 0 is met in step1,
i.e. when the timer reaches 3.
- echo 0x2000 > /sys/bus/amba/devices/<tgu-name>/step1_condition_select/default
Note:
1. 'default' register allows for establishing the resultant action for
the default condition
2. Trigger:For each possible output trigger available from
the Design document, there are three triggers: interrupts, CTI,
and Cross-TGU mapping.All three triggers can occur, but
the choice of which trigger to use depends on the user's
needs.
8. Compare the timer to 3 in step 1.
- echo 0x3 > /sys/bus/amba/devices/<tgu-name>/step1_timer/reg0
9. enale tgu
- echo 1 > /sys/bus/amba/devices/<tgu-name>/enable_tgu
---
Link to V14: https://lore.kernel.org/all/20260417073336.2712426-1-songwei.chai@oss.qualcomm.com/
Changes in V15:
- Add a maintainer for "drivers/hwtracing/qcom" in MAINTAINERS
---
Link to V13: https://lore.kernel.org/all/20260402092838.341295-1-songwei.chai@oss.qualcomm.com/
Changes in V14:
- Fix some typos and formatting.
---
Link to V12: https://lore.kernel.org/all/20260317032639.2393221-1-songwei.chai@oss.qualcomm.com/
Changes in V13:
- add ":" after "KernelVersion"
- add an enablement check in the enable function to avoid increasing the counter each time
---
Link to V11: https://lore.kernel.org/all/ee1ca8e6-8e5f-47d8-8a24-f904ee2fc6d0@oss.qualcomm.com/
Changes in V12:
- Remove the in-ports property from the bindings, as this device is decoupled from CoreSight.
- Update kernel version and date.
---
Link to V10: https://lore.kernel.org/all/20c5406d-3e9f-4fdb-84ba-4cbe629c79b5@oss.qualcomm.com/
Changes in V11:
- Change the names of members in drvdata: max_xxx -> num_xxx, enable -> enabled
- Use "FIELD_GET" to replace "BMVAL"
- Use devm_kcalloc to replace devm_kzalloc once create members of value_table
- Keep a consistent \n above return
- Keep reverse-Christmas-tree style
- Add checks so that the enable and reset nodes only accept 0 or 1
---
Link to V9: https://lore.kernel.org/all/20251219065902.2296896-1-songwei.chai@oss.qualcomm.com/
Changes in V10:
- Modified code formatting based on Jie's feedback to improve readability.
- Applied inverse Christmas tree order to the variables.
---
Link to V8: https://lore.kernel.org/all/20251203090055.2432719-1-songwei.chai@oss.qualcomm.com/
Changes in V9:
- Decoupled the tgu driver from coresight header file and registered it as an amba device.
- Retained Rob's reviewed-by tag on patch1/7 since the file remains unchanged.
- Updated the sysfs node path in the Documentation directory.
---
Link to V7: https://lore.kernel.org/all/20251104064043.88972-1-songwei.chai@oss.qualcomm.com/
Changes in V8:
- Add "select" section in bindings.
- Update publish date in "sysfs-bus-coresight-devices-tgu".
---
Link to V6: https://lore.kernel.org/all/20250709104114.22240-1-songchai@qti.qualcomm.com/
Changes in V7:
- Move the TGU code location from 'drivers/hwtracing/coresight/' to 'drivers/hwtracing/qcom/'.
- Rename the spinlock used in the code from 'spinlock' to 'lock'.
- Perform the 'calculate_array_location' separately, instead of doing it within the function.
- Update the sender email address.
---
Link to V5: https://lore.kernel.org/all/20250529081949.26493-1-quic_songchai@quicinc.com/
Changes in V6:
- Replace spinlock with guard(spinlock) in tgu_enable.
- Remove redundant blank line.
- Update publish date and contact member's name in "sysfs-bus-coresight-devices-tgu".
---
Link to V4: https://patchwork.kernel.org/project/linux-arm-msm/cover/20250423101054.954066-1-quic_songchai@quicinc.com/
Changes in V5:
- Update publish date and kernel_version in "sysfs-bus-coresight-devices-tgu"
---
Link to V3: https://lore.kernel.org/all/20250227092640.2666894-1-quic_songchai@quicinc.com/
Changes in V4:
- Add changlog in coverletter.
- Correct 'year' in Copyright in patch1.
- Correct port mechansim description in patch1.
- Remove 'tgu-steps','tgu-regs','tgu-conditions','tgu-timer-counters' from dt-binding
and set them through reading DEVID register as per Mike's suggestion.
- Modify tgu_disable func to make it have single return point in patch2 as per
Mike's suggestion.
- Use sysfs_emit in enable_tgu_show func in ptach2.
- Remove redundant judgement in enable_tgu_store in patch2.
- Correct typo in description in patch3.
- Set default ret as SYSFS_GROUP_INVISIBLE, and returnret at end in pacth3 as
per Mike's suggestion.
- Remove tgu_dataset_ro definition in patch3
- Use #define constants with explanations of what they are rather than
arbitrary magic numbers in patch3 and patch4.
- Check -EINVAL before using 'calculate_array_location()' in array in patch4.
- Add 'default' in 'tgu_dataset_show''s switch part in patch4.
- Document the value needed to initiate the reset in pacth7.
- Check "value" in 'reset_tgu_store' and bail out with an error code if 0 in patch7.
- Remove dev_dbg in 'reset_tgu_store' in patch7.
---
Link to V2: https://lore.kernel.org/all/20241010073917.16023-1-quic_songchai@quicinc.com/
Changes in V3:
- Correct typo and format in dt-binding in patch1
- Rebase to the latest kernel version
---
Link to V1: https://lore.kernel.org/all/20240830092311.14400-1-quic_songchai@quicinc.com/
Changes in V2:
- Use real name instead of login name,
- Correct typo and format in dt-binding and code.
- Bring order in tgu_prob(declarations with and without assignments) as per
Krzysztof's suggestion.
- Add module device table in patch2.
- Set const for tgu_common_grp and tgu_ids in patch2.
- Initialize 'data' in tgu_ids to fix the warning in pacth2.
---
Songwei Chai (7):
dt-bindings: arm: Add support for Qualcomm TGU trace
qcom-tgu: Add TGU driver
qcom-tgu: Add signal priority support
qcom-tgu: Add TGU decode support
qcom-tgu: Add support to configure next action
qcom-tgu: Add timer/counter functionality for TGU
qcom-tgu: Add reset node to initialize
.../ABI/testing/sysfs-bus-amba-devices-tgu | 51 ++
.../devicetree/bindings/arm/qcom,tgu.yaml | 71 ++
MAINTAINERS | 11 +
drivers/Makefile | 1 +
drivers/hwtracing/Kconfig | 2 +
drivers/hwtracing/qcom/Kconfig | 20 +
drivers/hwtracing/qcom/Makefile | 3 +
drivers/hwtracing/qcom/tgu.c | 704 ++++++++++++++++++
drivers/hwtracing/qcom/tgu.h | 275 +++++++
9 files changed, 1138 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
create mode 100644 Documentation/devicetree/bindings/arm/qcom,tgu.yaml
create mode 100644 drivers/hwtracing/qcom/Kconfig
create mode 100644 drivers/hwtracing/qcom/Makefile
create mode 100644 drivers/hwtracing/qcom/tgu.c
create mode 100644 drivers/hwtracing/qcom/tgu.h
--
2.34.1
^ permalink raw reply
* [PATCH v15 1/7] dt-bindings: arm: Add support for Qualcomm TGU trace
From: Songwei Chai @ 2026-07-01 5:55 UTC (permalink / raw)
To: andersson, alexander.shishkin, mike.leach, konrad.dybcio,
suzuki.poulose, james.clark, krzk+dt, conor+dt
Cc: Songwei Chai, linux-kernel, linux-arm-kernel, linux-arm-msm,
coresight, devicetree, gregkh, Rob Herring
In-Reply-To: <20260701055529.445819-1-songwei.chai@oss.qualcomm.com>
The Trigger Generation Unit (TGU) is designed to detect patterns or
sequences within a specific region of the System on Chip (SoC). Once
configured and activated, it monitors sense inputs and can detect a
pre-programmed state or sequence across clock cycles, subsequently
producing a trigger.
TGU configuration space
offset table
x-------------------------x
| |
| |
| | Step configuration
| | space layout
| coresight management | x-------------x
| registers | |---> | |
| | | | reserve |
| | | | |
|-------------------------| | |-------------|
| | | | priority[3] |
| step[7] |<-- | |-------------|
|-------------------------| | | | priority[2] |
| | | | |-------------|
| ... | |Steps region | | priority[1] |
| | | | |-------------|
|-------------------------| | | | priority[0] |
| |<-- | |-------------|
| step[0] |--------------------> | |
|-------------------------| | condition |
| | | |
| control and status | x-------------x
| space | | |
x-------------------------x |Timer/Counter|
| |
x-------------x
TGU Configuration in Hardware
The TGU provides a step region for user configuration, similar
to a flow chart. Each step region consists of three register clusters:
1.Priority Region: Sets the required signals with priority.
2.Condition Region: Defines specific requirements (e.g., signal A
reaches three times) and the subsequent action once the requirement is
met.
3.Timer/Counter (Optional): Provides timing or counting functionality.
Add a new tgu.yaml file to describe the bindings required to
define the TGU in the device trees.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
---
.../devicetree/bindings/arm/qcom,tgu.yaml | 71 +++++++++++++++++++
1 file changed, 71 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/qcom,tgu.yaml
diff --git a/Documentation/devicetree/bindings/arm/qcom,tgu.yaml b/Documentation/devicetree/bindings/arm/qcom,tgu.yaml
new file mode 100644
index 000000000000..76440f2497b9
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/qcom,tgu.yaml
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+# Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/qcom,tgu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Trigger Generation Unit - TGU
+
+description: |
+ The Trigger Generation Unit (TGU) is a Data Engine which can be utilized
+ to sense a plurality of signals and create a trigger into the CTI or
+ generate interrupts to processors. The TGU is like the trigger circuit
+ of a Logic Analyzer. The corresponding trigger logic can be realized by
+ configuring the conditions for each step after sensing the signal.
+ Once setup and enabled, it will observe sense inputs and based upon
+ the activity of those inputs, even over clock cycles, may detect a
+ 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 identify the issue in time
+ once there is abnormal behavior in the subsystem.
+
+maintainers:
+ - Mao Jinlong <jinlong.mao@oss.qualcomm.com>
+ - Songwei Chai <songwei.chai@oss.qualcomm.com>
+
+# Need a custom select here or 'arm,primecell' will match on lots of nodes
+select:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,tgu
+ required:
+ - compatible
+
+properties:
+ compatible:
+ items:
+ - const: qcom,tgu
+ - const: arm,primecell
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ items:
+ - const: apb_pclk
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ tgu@10b0e000 {
+ compatible = "qcom,tgu", "arm,primecell";
+ reg = <0x10b0e000 0x1000>;
+
+ clocks = <&aoss_qmp>;
+ clock-names = "apb_pclk";
+ };
+...
--
2.34.1
^ permalink raw reply related
* [PATCH v15 2/7] qcom-tgu: Add TGU driver
From: Songwei Chai @ 2026-07-01 5:55 UTC (permalink / raw)
To: andersson, alexander.shishkin, mike.leach, konrad.dybcio,
suzuki.poulose, james.clark, krzk+dt, conor+dt
Cc: Songwei Chai, linux-kernel, linux-arm-kernel, linux-arm-msm,
coresight, devicetree, gregkh, Jie Gan
In-Reply-To: <20260701055529.445819-1-songwei.chai@oss.qualcomm.com>
Add driver to support device TGU (Trigger Generation Unit).
TGU is a Data Engine which can be utilized to sense a plurality of
signals and create a trigger into the CTI or generate interrupts to
processors. Add probe/enable/disable functions for tgu.
Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
Acked-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
.../ABI/testing/sysfs-bus-amba-devices-tgu | 9 +
MAINTAINERS | 11 +
drivers/Makefile | 1 +
drivers/hwtracing/Kconfig | 2 +
drivers/hwtracing/qcom/Kconfig | 20 ++
drivers/hwtracing/qcom/Makefile | 3 +
drivers/hwtracing/qcom/tgu.c | 193 ++++++++++++++++++
drivers/hwtracing/qcom/tgu.h | 51 +++++
8 files changed, 290 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
create mode 100644 drivers/hwtracing/qcom/Kconfig
create mode 100644 drivers/hwtracing/qcom/Makefile
create mode 100644 drivers/hwtracing/qcom/tgu.c
create mode 100644 drivers/hwtracing/qcom/tgu.h
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 000000000000..8eddcf3bb5fe
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
@@ -0,0 +1,9 @@
+What: /sys/bus/amba/devices/<tgu-name>/enable_tgu
+Date: July 2026
+KernelVersion: 7.2
+Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
+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.
diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a9..d627c27d0592 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22358,6 +22358,17 @@ S: Maintained
F: Documentation/tee/qtee.rst
F: drivers/tee/qcomtee/
+QUALCOMM HARDWARETRACING DRIVER
+M: Songwei Chai <songwei.chai@oss.qualcomm.com>
+M: Jie Gan <jie.gan@oss.qualcomm.com>
+M: Suzuki K Poulose <suzuki.poulose@arm.com>
+L: linux-arm-msm@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux.git
+F: Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
+F: Documentation/devicetree/bindings/arm/qcom,tgu.yaml
+F: drivers/hwtracing/qcom/
+
QUALCOMM TRUST ZONE MEMORY ALLOCATOR
M: Bartosz Golaszewski <brgl@kernel.org>
L: linux-arm-msm@vger.kernel.org
diff --git a/drivers/Makefile b/drivers/Makefile
index 0841ea851847..6a2cbe5b340f 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 911ee977103c..8a640218eed8 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/qcom/Kconfig b/drivers/hwtracing/qcom/Kconfig
new file mode 100644
index 000000000000..5c94c75ffa39
--- /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 000000000000..5a0a868c1ea0
--- /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 000000000000..49c8f710b931
--- /dev/null
+++ b/drivers/hwtracing/qcom/tgu.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/amba/bus.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+
+#include "tgu.h"
+
+static void tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
+{
+ TGU_UNLOCK(drvdata->base);
+ /* Enable TGU to program the triggers */
+ writel(1, drvdata->base + TGU_CONTROL);
+ TGU_LOCK(drvdata->base);
+}
+
+static int tgu_enable(struct device *dev)
+{
+ struct tgu_drvdata *drvdata = dev_get_drvdata(dev);
+
+ guard(spinlock)(&drvdata->lock);
+ drvdata->enabled = true;
+
+ tgu_write_all_hw_regs(drvdata);
+
+ return 0;
+}
+
+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);
+
+static struct attribute *tgu_common_attrs[] = {
+ &dev_attr_enable_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,
+ NULL,
+};
+
+static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct tgu_drvdata *drvdata;
+ 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);
+
+ 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->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 <songwei.chai@oss.qualcomm.com>");
+MODULE_AUTHOR("Jinlong Mao <jinlong.mao@oss.qualcomm.com>");
+MODULE_DESCRIPTION("Qualcomm Trigger Generation Unit driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwtracing/qcom/tgu.h b/drivers/hwtracing/qcom/tgu.h
new file mode 100644
index 000000000000..dd7533b9d735
--- /dev/null
+++ b/drivers/hwtracing/qcom/tgu.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef _QCOM_TGU_H
+#define _QCOM_TGU_H
+
+/* Register addresses */
+#define TGU_CONTROL 0x0000
+#define TGU_LAR 0xfb0
+#define TGU_UNLOCK_OFFSET 0xc5acce55
+
+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
+ * @lock: Spinlock for handling concurrent access to private data
+ * @enabled: Flag indicating whether the TGU device is enabled
+ *
+ * This structure defines the data associated with a TGU device,
+ * including its base address, device pointers, clock, spinlock for
+ * synchronization, trigger data pointers, maximum limits for various
+ * trigger-related parameters, and enable status.
+ */
+struct tgu_drvdata {
+ void __iomem *base;
+ struct device *dev;
+ spinlock_t lock;
+ bool enabled;
+};
+
+#endif
--
2.34.1
^ permalink raw reply related
* [PATCH v15 4/7] qcom-tgu: Add TGU decode support
From: Songwei Chai @ 2026-07-01 5:55 UTC (permalink / raw)
To: andersson, alexander.shishkin, mike.leach, konrad.dybcio,
suzuki.poulose, james.clark, krzk+dt, conor+dt
Cc: Songwei Chai, linux-kernel, linux-arm-kernel, linux-arm-msm,
coresight, devicetree, gregkh, Jie Gan
In-Reply-To: <20260701055529.445819-1-songwei.chai@oss.qualcomm.com>
Decoding is when all the potential pieces for creating a trigger
are brought together for a given step. Example - there may be a
counter keeping track of some occurrences and a priority-group that
is being used to detect a pattern on the sense inputs. These 2
inputs to condition_decode must be programmed, for a given step,
to establish the condition for the trigger, or movement to another
steps.
Reviewed-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
---
.../ABI/testing/sysfs-bus-amba-devices-tgu | 7 +
drivers/hwtracing/qcom/tgu.c | 157 +++++++++++++++---
drivers/hwtracing/qcom/tgu.h | 27 +++
3 files changed, 170 insertions(+), 21 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
index 2f1d4ecaf76b..81a4e6036b72 100644
--- a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
+++ b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
@@ -14,3 +14,10 @@ KernelVersion: 7.2
Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
Description:
(RW) Set/Get the sensed signal with specific step and priority for TGU.
+
+What: /sys/bus/amba/devices/<tgu-name>/step[0:7]_condition_decode/reg[0:3]
+Date: July 2026
+KernelVersion: 7.2
+Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
+Description:
+ (RW) Set/Get the decode mode with specific step for TGU.
diff --git a/drivers/hwtracing/qcom/tgu.c b/drivers/hwtracing/qcom/tgu.c
index 7d69986c3e3d..937211923d93 100644
--- a/drivers/hwtracing/qcom/tgu.c
+++ b/drivers/hwtracing/qcom/tgu.c
@@ -18,8 +18,33 @@ static int calculate_array_location(struct tgu_drvdata *drvdata,
int step_index, int operation_index,
int reg_index)
{
- return operation_index * (drvdata->num_step) * (drvdata->num_reg) +
- step_index * (drvdata->num_reg) + 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;
+ 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,
@@ -30,12 +55,26 @@ static ssize_t tgu_dataset_show(struct device *dev,
container_of(attr, struct tgu_attribute, attr);
int index;
- index = calculate_array_location(drvdata, tgu_attr->step_index,
- tgu_attr->operation_index,
- tgu_attr->reg_num);
-
- return sysfs_emit(buf, "0x%x\n",
- drvdata->value_table->priority[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]);
+ default:
+ break;
+ }
+ return -EINVAL;
}
static ssize_t tgu_dataset_store(struct device *dev,
@@ -54,13 +93,31 @@ static ssize_t tgu_dataset_store(struct device *dev,
return ret;
guard(spinlock)(&tgu_drvdata->lock);
- index = calculate_array_location(tgu_drvdata, tgu_attr->step_index,
+ index = check_array_location(tgu_drvdata, tgu_attr->step_index,
tgu_attr->operation_index,
tgu_attr->reg_num);
- tgu_drvdata->value_table->priority[index] = val;
+ 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;
+ default:
+ ret = -EINVAL;
+ break;
+ }
- return size;
+ return ret;
}
static umode_t tgu_node_visible(struct kobject *kobject,
@@ -77,13 +134,26 @@ static umode_t tgu_node_visible(struct kobject *kobject,
if (tgu_attr->step_index >= drvdata->num_step)
return SYSFS_GROUP_INVISIBLE;
- if (tgu_attr->reg_num >= drvdata->num_reg)
- return 0;
+ 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;
+ default:
+ break;
+ }
- return attr->mode;
+ return 0;
}
-static void tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
+static ssize_t tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
{
int i, j, k, index;
@@ -91,8 +161,10 @@ static void tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
for (i = 0; i < drvdata->num_step; i++) {
for (j = 0; j < MAX_PRIORITY; j++) {
for (k = 0; k < drvdata->num_reg; k++) {
- index = calculate_array_location(
+ index = check_array_location(
drvdata, i, j, k);
+ if (index == -EINVAL)
+ goto exit;
writel(drvdata->value_table->priority[index],
drvdata->base +
@@ -100,9 +172,23 @@ static void tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
}
}
}
+
+ 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));
+ }
+ }
/* 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)
@@ -131,16 +217,26 @@ static void tgu_set_steps(struct tgu_drvdata *drvdata)
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);
+}
+
static int tgu_enable(struct device *dev)
{
struct tgu_drvdata *drvdata = dev_get_drvdata(dev);
+ int ret;
guard(spinlock)(&drvdata->lock);
- drvdata->enabled = true;
- tgu_write_all_hw_regs(drvdata);
+ ret = tgu_write_all_hw_regs(drvdata);
+ if (!ret)
+ drvdata->enabled = true;
- return 0;
+ return ret;
}
static void tgu_do_disable(struct tgu_drvdata *drvdata)
@@ -262,6 +358,14 @@ static const struct attribute_group *tgu_attr_groups[] = {
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),
NULL,
};
@@ -269,8 +373,8 @@ 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;
- size_t priority_size;
+ unsigned int *priority, *condition;
+ size_t priority_size, condition_size;
int ret;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
@@ -288,6 +392,7 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
tgu_set_reg_number(drvdata);
tgu_set_steps(drvdata);
+ tgu_set_conditions(drvdata);
ret = sysfs_create_groups(&dev->kobj, tgu_attr_groups);
if (ret) {
@@ -310,6 +415,16 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
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;
+
drvdata->enabled = false;
pm_runtime_put(&adev->dev);
diff --git a/drivers/hwtracing/qcom/tgu.h b/drivers/hwtracing/qcom/tgu.h
index f994d83acb1d..56e4161a8bc2 100644
--- a/drivers/hwtracing/qcom/tgu.h
+++ b/drivers/hwtracing/qcom/tgu.h
@@ -16,6 +16,8 @@
((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_BITS_PER_SIGNAL 4
#define LENGTH_REGISTER 32
@@ -49,6 +51,7 @@
*/
#define STEP_OFFSET 0x1D8
#define PRIORITY_START_OFFSET 0x0074
+#define CONDITION_DECODE_OFFSET 0x0050
#define PRIORITY_OFFSET 0x60
#define REG_OFFSET 0x4
@@ -57,6 +60,9 @@
(PRIORITY_START_OFFSET + PRIORITY_OFFSET * priority +\
REG_OFFSET * reg + STEP_OFFSET * step)
+#define CONDITION_DECODE_STEP(step, decode) \
+ (CONDITION_DECODE_OFFSET + REG_OFFSET * decode + 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), \
@@ -68,6 +74,8 @@
#define STEP_PRIORITY(step_index, reg_num, priority) \
tgu_dataset_rw(reg##reg_num, step_index, TGU_PRIORITY##priority, \
reg_num)
+#define STEP_DECODE(step_index, reg_num) \
+ tgu_dataset_rw(reg##reg_num, step_index, TGU_CONDITION_DECODE, reg_num)
#define STEP_PRIORITY_LIST(step_index, priority) \
{STEP_PRIORITY(step_index, 0, priority), \
@@ -91,6 +99,14 @@
NULL \
}
+#define STEP_DECODE_LIST(n) \
+ {STEP_DECODE(n, 0), \
+ STEP_DECODE(n, 1), \
+ STEP_DECODE(n, 2), \
+ STEP_DECODE(n, 3), \
+ NULL \
+ }
+
#define PRIORITY_ATTRIBUTE_GROUP_INIT(step, priority)\
(&(const struct attribute_group){\
.attrs = (struct attribute*[])STEP_PRIORITY_LIST(step, priority),\
@@ -98,11 +114,19 @@
.name = "step" #step "_priority" #priority \
})
+#define CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(step)\
+ (&(const struct attribute_group){\
+ .attrs = (struct attribute*[])STEP_DECODE_LIST(step),\
+ .is_visible = tgu_node_visible,\
+ .name = "step" #step "_condition_decode" \
+ })
+
enum operation_index {
TGU_PRIORITY0,
TGU_PRIORITY1,
TGU_PRIORITY2,
TGU_PRIORITY3,
+ TGU_CONDITION_DECODE,
};
/* Maximum priority that TGU supports */
@@ -117,6 +141,7 @@ struct tgu_attribute {
struct value_table {
unsigned int *priority;
+ unsigned int *condition_decode;
};
static inline void TGU_LOCK(void __iomem *addr)
@@ -146,6 +171,7 @@ static inline void TGU_UNLOCK(void __iomem *addr)
* @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
*
* This structure defines the data associated with a TGU device,
* including its base address, device pointers, clock, spinlock for
@@ -160,6 +186,7 @@ struct tgu_drvdata {
struct value_table *value_table;
int num_reg;
int num_step;
+ int num_condition_decode;
};
#endif
--
2.34.1
^ permalink raw reply related
* [PATCH v15 3/7] qcom-tgu: Add signal priority support
From: Songwei Chai @ 2026-07-01 5:55 UTC (permalink / raw)
To: andersson, alexander.shishkin, mike.leach, konrad.dybcio,
suzuki.poulose, james.clark, krzk+dt, conor+dt
Cc: Songwei Chai, linux-kernel, linux-arm-kernel, linux-arm-msm,
coresight, devicetree, gregkh, Jie Gan
In-Reply-To: <20260701055529.445819-1-songwei.chai@oss.qualcomm.com>
Like circuit of a Logic analyzer, in TGU, the requirement could be
configured in each step and the trigger will be created once the
requirements are met. Add priority functionality here to sort the
signals into different priorities. The signal which is wanted could
be configured in each step's priority node, the larger number means
the higher priority and the signal with higher priority will be sensed
more preferentially.
Reviewed-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
---
.../ABI/testing/sysfs-bus-amba-devices-tgu | 7 +
drivers/hwtracing/qcom/tgu.c | 161 ++++++++++++++++++
drivers/hwtracing/qcom/tgu.h | 114 +++++++++++++
3 files changed, 282 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
index 8eddcf3bb5fe..2f1d4ecaf76b 100644
--- a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
+++ b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
@@ -7,3 +7,10 @@ Description:
Accepts only one of the 2 values - 0 or 1.
0 : disable TGU.
1 : enable TGU.
+
+What: /sys/bus/amba/devices/<tgu-name>/step[0:7]_priority[0:3]/reg[0:17]
+Date: July 2026
+KernelVersion: 7.2
+Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
+Description:
+ (RW) Set/Get the sensed signal with specific step and priority for TGU.
diff --git a/drivers/hwtracing/qcom/tgu.c b/drivers/hwtracing/qcom/tgu.c
index 49c8f710b931..7d69986c3e3d 100644
--- a/drivers/hwtracing/qcom/tgu.c
+++ b/drivers/hwtracing/qcom/tgu.c
@@ -14,14 +14,123 @@
#include "tgu.h"
+static int calculate_array_location(struct tgu_drvdata *drvdata,
+ int step_index, int operation_index,
+ int reg_index)
+{
+ return operation_index * (drvdata->num_step) * (drvdata->num_reg) +
+ step_index * (drvdata->num_reg) + reg_index;
+}
+
+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 = calculate_array_location(drvdata, tgu_attr->step_index,
+ tgu_attr->operation_index,
+ tgu_attr->reg_num);
+
+ return sysfs_emit(buf, "0x%x\n",
+ drvdata->value_table->priority[index]);
+}
+
+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 = calculate_array_location(tgu_drvdata, tgu_attr->step_index,
+ tgu_attr->operation_index,
+ tgu_attr->reg_num);
+
+ tgu_drvdata->value_table->priority[index] = val;
+
+ return size;
+}
+
+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;
+
+ if (tgu_attr->reg_num >= drvdata->num_reg)
+ return 0;
+
+ return attr->mode;
+}
+
static void 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 = calculate_array_location(
+ drvdata, i, j, k);
+
+ writel(drvdata->value_table->priority[index],
+ drvdata->base +
+ PRIORITY_REG_STEP(i, j, k));
+ }
+ }
+ }
/* Enable TGU to program the triggers */
writel(1, drvdata->base + TGU_CONTROL);
TGU_LOCK(drvdata->base);
}
+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 int tgu_enable(struct device *dev)
{
struct tgu_drvdata *drvdata = dev_get_drvdata(dev);
@@ -121,6 +230,38 @@ static const struct attribute_group tgu_common_grp = {
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),
NULL,
};
@@ -128,6 +269,8 @@ 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;
+ size_t priority_size;
int ret;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
@@ -143,12 +286,30 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
spin_lock_init(&drvdata->lock);
+ tgu_set_reg_number(drvdata);
+ tgu_set_steps(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;
+
drvdata->enabled = false;
pm_runtime_put(&adev->dev);
diff --git a/drivers/hwtracing/qcom/tgu.h b/drivers/hwtracing/qcom/tgu.h
index dd7533b9d735..f994d83acb1d 100644
--- a/drivers/hwtracing/qcom/tgu.h
+++ b/drivers/hwtracing/qcom/tgu.h
@@ -10,6 +10,114 @@
#define TGU_CONTROL 0x0000
#define TGU_LAR 0xfb0
#define TGU_UNLOCK_OFFSET 0xc5acce55
+#define TGU_DEVID 0xfc8
+
+#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_BITS_PER_SIGNAL 4
+#define LENGTH_REGISTER 32
+
+/*
+ * TGU configuration space Step configuration
+ * offset table space layout
+ * x-------------------------x$ x-------------x$
+ * | |$ | |$
+ * | | | reserve |$
+ * | | | |$
+ * |coresight management | |-------------|base+n*0x1D8+0x1F4$
+ * | registers | |---> |priority[3] |$
+ * | | | |-------------|base+n*0x1D8+0x194$
+ * | | | |priority[2] |$
+ * |-------------------------| | |-------------|base+n*0x1D8+0x134$
+ * | | | |priority[1] |$
+ * | step[7] | | |-------------|base+n*0x1D8+0xD4$
+ * |-------------------------|->base+0x40+7*0x1D8 | |priority[0] |$
+ * | | | |-------------|base+n*0x1D8+0x74$
+ * | ... | | | condition |$
+ * | | | | select |$
+ * |-------------------------|->base+0x40+1*0x1D8 | |-------------|base+n*0x1D8+0x60$
+ * | | | | condition |$
+ * | step[0] |--------------------> | decode |$
+ * |-------------------------|-> base+0x40 |-------------|base+n*0x1D8+0x50$
+ * | | | |$
+ * | Control and status space| |Timer/Counter|$
+ * | space | | |$
+ * x-------------------------x->base x-------------x base+n*0x1D8+0x40$
+ *
+ */
+#define STEP_OFFSET 0x1D8
+#define PRIORITY_START_OFFSET 0x0074
+#define PRIORITY_OFFSET 0x60
+#define REG_OFFSET 0x4
+
+/* Calculate compare step addresses */
+#define PRIORITY_REG_STEP(step, priority, reg)\
+ (PRIORITY_START_OFFSET + PRIORITY_OFFSET * priority +\
+ REG_OFFSET * reg + 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), \
+ step_index, \
+ type, \
+ reg_num, \
+ } })[0].attr.attr)
+
+#define STEP_PRIORITY(step_index, reg_num, priority) \
+ tgu_dataset_rw(reg##reg_num, step_index, TGU_PRIORITY##priority, \
+ reg_num)
+
+#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, 3, priority), \
+ STEP_PRIORITY(step_index, 4, priority), \
+ STEP_PRIORITY(step_index, 5, priority), \
+ STEP_PRIORITY(step_index, 6, priority), \
+ STEP_PRIORITY(step_index, 7, priority), \
+ STEP_PRIORITY(step_index, 8, priority), \
+ STEP_PRIORITY(step_index, 9, priority), \
+ STEP_PRIORITY(step_index, 10, priority), \
+ STEP_PRIORITY(step_index, 11, priority), \
+ STEP_PRIORITY(step_index, 12, priority), \
+ STEP_PRIORITY(step_index, 13, priority), \
+ STEP_PRIORITY(step_index, 14, priority), \
+ STEP_PRIORITY(step_index, 15, priority), \
+ STEP_PRIORITY(step_index, 16, priority), \
+ STEP_PRIORITY(step_index, 17, priority), \
+ NULL \
+ }
+
+#define PRIORITY_ATTRIBUTE_GROUP_INIT(step, priority)\
+ (&(const struct attribute_group){\
+ .attrs = (struct attribute*[])STEP_PRIORITY_LIST(step, priority),\
+ .is_visible = tgu_node_visible,\
+ .name = "step" #step "_priority" #priority \
+ })
+
+enum operation_index {
+ TGU_PRIORITY0,
+ TGU_PRIORITY1,
+ TGU_PRIORITY2,
+ TGU_PRIORITY3,
+};
+
+/* Maximum priority that TGU supports */
+#define MAX_PRIORITY 4
+
+struct tgu_attribute {
+ struct device_attribute attr;
+ u32 step_index;
+ enum operation_index operation_index;
+ u32 reg_num;
+};
+
+struct value_table {
+ unsigned int *priority;
+};
static inline void TGU_LOCK(void __iomem *addr)
{
@@ -35,6 +143,9 @@ static inline void TGU_UNLOCK(void __iomem *addr)
* @dev: Pointer to the associated device structure
* @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
*
* This structure defines the data associated with a TGU device,
* including its base address, device pointers, clock, spinlock for
@@ -46,6 +157,9 @@ struct tgu_drvdata {
struct device *dev;
spinlock_t lock;
bool enabled;
+ struct value_table *value_table;
+ int num_reg;
+ int num_step;
};
#endif
--
2.34.1
^ permalink raw reply related
* [PATCH v15 5/7] qcom-tgu: Add support to configure next action
From: Songwei Chai @ 2026-07-01 5:55 UTC (permalink / raw)
To: andersson, alexander.shishkin, mike.leach, konrad.dybcio,
suzuki.poulose, james.clark, krzk+dt, conor+dt
Cc: Songwei Chai, linux-kernel, linux-arm-kernel, linux-arm-msm,
coresight, devicetree, gregkh, Jie Gan
In-Reply-To: <20260701055529.445819-1-songwei.chai@oss.qualcomm.com>
Add "select" node for each step to determine if another step is taken,
trigger(s) are generated, counters/timers incremented/decremented, etc.
Reviewed-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
---
.../ABI/testing/sysfs-bus-amba-devices-tgu | 7 +++
drivers/hwtracing/qcom/tgu.c | 53 ++++++++++++++++++-
drivers/hwtracing/qcom/tgu.h | 26 +++++++++
3 files changed, 84 insertions(+), 2 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
index 81a4e6036b72..f8b4ea199faf 100644
--- a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
+++ b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
@@ -21,3 +21,10 @@ KernelVersion: 7.2
Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
Description:
(RW) Set/Get the decode mode with specific step for TGU.
+
+What: /sys/bus/amba/devices/<tgu-name>/step[0:7]_condition_select/reg[0:3]
+Date: July 2026
+KernelVersion: 7.2
+Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
+Description:
+ (RW) Set/Get the next action with specific step for TGU.
diff --git a/drivers/hwtracing/qcom/tgu.c b/drivers/hwtracing/qcom/tgu.c
index 937211923d93..fefe932059cb 100644
--- a/drivers/hwtracing/qcom/tgu.c
+++ b/drivers/hwtracing/qcom/tgu.c
@@ -29,6 +29,9 @@ static int calculate_array_location(struct tgu_drvdata *drvdata,
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;
default:
break;
}
@@ -71,6 +74,9 @@ static ssize_t tgu_dataset_show(struct device *dev,
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]);
default:
break;
}
@@ -112,6 +118,10 @@ static ssize_t tgu_dataset_store(struct device *dev,
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;
default:
ret = -EINVAL;
break;
@@ -146,6 +156,13 @@ static umode_t tgu_node_visible(struct kobject *kobject,
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;
default:
break;
}
@@ -184,6 +201,18 @@ static ssize_t tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
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));
+ }
+ }
/* Enable TGU to program the triggers */
writel(1, drvdata->base + TGU_CONTROL);
exit:
@@ -223,6 +252,8 @@ static void tgu_set_conditions(struct tgu_drvdata *drvdata)
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 int tgu_enable(struct device *dev)
@@ -366,6 +397,14 @@ static const struct attribute_group *tgu_attr_groups[] = {
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),
NULL,
};
@@ -373,8 +412,8 @@ 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;
- size_t priority_size, condition_size;
+ unsigned int *priority, *condition, *select;
+ size_t priority_size, condition_size, select_size;
int ret;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
@@ -425,6 +464,16 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
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;
+
drvdata->enabled = false;
pm_runtime_put(&adev->dev);
diff --git a/drivers/hwtracing/qcom/tgu.h b/drivers/hwtracing/qcom/tgu.h
index 56e4161a8bc2..c61aa8dc51b0 100644
--- a/drivers/hwtracing/qcom/tgu.h
+++ b/drivers/hwtracing/qcom/tgu.h
@@ -52,6 +52,7 @@
#define STEP_OFFSET 0x1D8
#define PRIORITY_START_OFFSET 0x0074
#define CONDITION_DECODE_OFFSET 0x0050
+#define CONDITION_SELECT_OFFSET 0x0060
#define PRIORITY_OFFSET 0x60
#define REG_OFFSET 0x4
@@ -63,6 +64,9 @@
#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 tgu_dataset_rw(name, step_index, type, reg_num) \
(&((struct tgu_attribute[]){ { \
__ATTR(name, 0644, tgu_dataset_show, tgu_dataset_store), \
@@ -76,6 +80,8 @@
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_PRIORITY_LIST(step_index, priority) \
{STEP_PRIORITY(step_index, 0, priority), \
@@ -107,6 +113,15 @@
NULL \
}
+#define STEP_SELECT_LIST(n) \
+ {STEP_SELECT(n, 0), \
+ STEP_SELECT(n, 1), \
+ STEP_SELECT(n, 2), \
+ STEP_SELECT(n, 3), \
+ STEP_SELECT(n, 4), \
+ NULL \
+ }
+
#define PRIORITY_ATTRIBUTE_GROUP_INIT(step, priority)\
(&(const struct attribute_group){\
.attrs = (struct attribute*[])STEP_PRIORITY_LIST(step, priority),\
@@ -121,12 +136,20 @@
.name = "step" #step "_condition_decode" \
})
+#define CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(step)\
+ (&(const struct attribute_group){\
+ .attrs = (struct attribute*[])STEP_SELECT_LIST(step),\
+ .is_visible = tgu_node_visible,\
+ .name = "step" #step "_condition_select" \
+ })
+
enum operation_index {
TGU_PRIORITY0,
TGU_PRIORITY1,
TGU_PRIORITY2,
TGU_PRIORITY3,
TGU_CONDITION_DECODE,
+ TGU_CONDITION_SELECT,
};
/* Maximum priority that TGU supports */
@@ -142,6 +165,7 @@ struct tgu_attribute {
struct value_table {
unsigned int *priority;
unsigned int *condition_decode;
+ unsigned int *condition_select;
};
static inline void TGU_LOCK(void __iomem *addr)
@@ -172,6 +196,7 @@ static inline void TGU_UNLOCK(void __iomem *addr)
* @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
*
* This structure defines the data associated with a TGU device,
* including its base address, device pointers, clock, spinlock for
@@ -187,6 +212,7 @@ struct tgu_drvdata {
int num_reg;
int num_step;
int num_condition_decode;
+ int num_condition_select;
};
#endif
--
2.34.1
^ permalink raw reply related
* [PATCH v15 7/7] qcom-tgu: Add reset node to initialize
From: Songwei Chai @ 2026-07-01 5:55 UTC (permalink / raw)
To: andersson, alexander.shishkin, mike.leach, konrad.dybcio,
suzuki.poulose, james.clark, krzk+dt, conor+dt
Cc: Songwei Chai, linux-kernel, linux-arm-kernel, linux-arm-msm,
coresight, devicetree, gregkh, Jie Gan
In-Reply-To: <20260701055529.445819-1-songwei.chai@oss.qualcomm.com>
Add reset node to initialize the value of
priority/condition_decode/condition_select/timer/counter nodes.
Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
Reviewed-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
.../ABI/testing/sysfs-bus-amba-devices-tgu | 7 ++
drivers/hwtracing/qcom/tgu.c | 74 +++++++++++++++++++
2 files changed, 81 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
index 7fad11eb759c..cb0710fea9c7 100644
--- a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
+++ b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
@@ -42,3 +42,10 @@ KernelVersion: 7.2
Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
Description:
(RW) Set/Get the counter value with specific step for TGU.
+
+What: /sys/bus/amba/devices/<tgu-name>/reset_tgu
+Date: July 2026
+KernelVersion: 7.2
+Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
+Description:
+ (Write) Write 1 to reset the dataset for TGU.
diff --git a/drivers/hwtracing/qcom/tgu.c b/drivers/hwtracing/qcom/tgu.c
index 6d5bf2621cb0..9fb51f2a912f 100644
--- a/drivers/hwtracing/qcom/tgu.c
+++ b/drivers/hwtracing/qcom/tgu.c
@@ -420,8 +420,82 @@ static ssize_t enable_tgu_store(struct device *dev,
}
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,
};
--
2.34.1
^ permalink raw reply related
* [PATCH v15 6/7] qcom-tgu: Add timer/counter functionality for TGU
From: Songwei Chai @ 2026-07-01 5:55 UTC (permalink / raw)
To: andersson, alexander.shishkin, mike.leach, konrad.dybcio,
suzuki.poulose, james.clark, krzk+dt, conor+dt
Cc: Songwei Chai, linux-kernel, linux-arm-kernel, linux-arm-msm,
coresight, devicetree, gregkh, Jie Gan
In-Reply-To: <20260701055529.445819-1-songwei.chai@oss.qualcomm.com>
Add counter and timer node for each step which could be
programed if they are to be utilized in trigger event/sequence.
Reviewed-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
---
.../ABI/testing/sysfs-bus-amba-devices-tgu | 14 +++
drivers/hwtracing/qcom/tgu.c | 116 +++++++++++++++++-
drivers/hwtracing/qcom/tgu.h | 57 +++++++++
3 files changed, 185 insertions(+), 2 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
index f8b4ea199faf..7fad11eb759c 100644
--- a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
+++ b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
@@ -28,3 +28,17 @@ KernelVersion: 7.2
Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
Description:
(RW) Set/Get the next action with specific step for TGU.
+
+What: /sys/bus/amba/devices/<tgu-name>/step[0:7]_timer/reg[0:1]
+Date: July 2026
+KernelVersion: 7.2
+Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
+Description:
+ (RW) Set/Get the timer value with specific step for TGU.
+
+What: /sys/bus/amba/devices/<tgu-name>/step[0:7]_counter/reg[0:1]
+Date: July 2026
+KernelVersion: 7.2
+Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
+Description:
+ (RW) Set/Get the counter value with specific step for TGU.
diff --git a/drivers/hwtracing/qcom/tgu.c b/drivers/hwtracing/qcom/tgu.c
index fefe932059cb..6d5bf2621cb0 100644
--- a/drivers/hwtracing/qcom/tgu.c
+++ b/drivers/hwtracing/qcom/tgu.c
@@ -32,6 +32,10 @@ static int calculate_array_location(struct tgu_drvdata *drvdata,
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;
}
@@ -77,6 +81,12 @@ static ssize_t tgu_dataset_show(struct device *dev,
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;
}
@@ -122,6 +132,14 @@ static ssize_t tgu_dataset_store(struct device *dev,
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;
@@ -163,6 +181,18 @@ static umode_t tgu_node_visible(struct kobject *kobject,
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;
}
@@ -213,6 +243,30 @@ static ssize_t tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
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:
@@ -256,6 +310,27 @@ static void tgu_set_conditions(struct tgu_drvdata *drvdata)
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);
@@ -405,6 +480,22 @@ static const struct attribute_group *tgu_attr_groups[] = {
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,
};
@@ -412,8 +503,8 @@ 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;
- size_t priority_size, condition_size, select_size;
+ 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);
@@ -432,6 +523,7 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
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) {
@@ -474,6 +566,26 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
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);
diff --git a/drivers/hwtracing/qcom/tgu.h b/drivers/hwtracing/qcom/tgu.h
index c61aa8dc51b0..1bcbc99169de 100644
--- a/drivers/hwtracing/qcom/tgu.h
+++ b/drivers/hwtracing/qcom/tgu.h
@@ -11,6 +11,7 @@
#define TGU_LAR 0xfb0
#define TGU_UNLOCK_OFFSET 0xc5acce55
#define TGU_DEVID 0xfc8
+#define CORESIGHT_DEVID2 0xfc0
#define TGU_DEVID_SENSE_INPUT(devid_val) \
((int)FIELD_GET(GENMASK(17, 10), devid_val))
@@ -18,6 +19,16 @@
((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
@@ -53,6 +64,8 @@
#define PRIORITY_START_OFFSET 0x0074
#define CONDITION_DECODE_OFFSET 0x0050
#define CONDITION_SELECT_OFFSET 0x0060
+#define TIMER_START_OFFSET 0x0040
+#define COUNTER_START_OFFSET 0x0048
#define PRIORITY_OFFSET 0x60
#define REG_OFFSET 0x4
@@ -67,6 +80,12 @@
#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 tgu_dataset_rw(name, step_index, type, reg_num) \
(&((struct tgu_attribute[]){ { \
__ATTR(name, 0644, tgu_dataset_show, tgu_dataset_store), \
@@ -82,6 +101,10 @@
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), \
@@ -122,6 +145,18 @@
NULL \
}
+#define STEP_TIMER_LIST(n) \
+ {STEP_TIMER(n, 0), \
+ STEP_TIMER(n, 1), \
+ NULL \
+ }
+
+#define STEP_COUNTER_LIST(n) \
+ {STEP_COUNTER(n, 0), \
+ STEP_COUNTER(n, 1), \
+ NULL \
+ }
+
#define PRIORITY_ATTRIBUTE_GROUP_INIT(step, priority)\
(&(const struct attribute_group){\
.attrs = (struct attribute*[])STEP_PRIORITY_LIST(step, priority),\
@@ -143,6 +178,20 @@
.name = "step" #step "_condition_select" \
})
+#define TIMER_ATTRIBUTE_GROUP_INIT(step)\
+ (&(const struct attribute_group){\
+ .attrs = (struct attribute*[])STEP_TIMER_LIST(step),\
+ .is_visible = tgu_node_visible,\
+ .name = "step" #step "_timer" \
+ })
+
+#define COUNTER_ATTRIBUTE_GROUP_INIT(step)\
+ (&(const struct attribute_group){\
+ .attrs = (struct attribute*[])STEP_COUNTER_LIST(step),\
+ .is_visible = tgu_node_visible,\
+ .name = "step" #step "_counter" \
+ })
+
enum operation_index {
TGU_PRIORITY0,
TGU_PRIORITY1,
@@ -150,6 +199,8 @@ enum operation_index {
TGU_PRIORITY3,
TGU_CONDITION_DECODE,
TGU_CONDITION_SELECT,
+ TGU_TIMER,
+ TGU_COUNTER,
};
/* Maximum priority that TGU supports */
@@ -166,6 +217,8 @@ struct value_table {
unsigned int *priority;
unsigned int *condition_decode;
unsigned int *condition_select;
+ unsigned int *timer;
+ unsigned int *counter;
};
static inline void TGU_LOCK(void __iomem *addr)
@@ -197,6 +250,8 @@ static inline void TGU_UNLOCK(void __iomem *addr)
* @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
@@ -213,6 +268,8 @@ struct tgu_drvdata {
int num_step;
int num_condition_decode;
int num_condition_select;
+ int num_timer;
+ int num_counter;
};
#endif
--
2.34.1
^ permalink raw reply related
* [PATCH 0/2] fix clock refcount imbalance for all Coresight platform drivers
From: Jie Gan @ 2026-07-01 6:05 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin, Anshuman Khandual, Yeoreum Yun,
Yuanfang Zhang, Maxime Coquelin, Alexandre Torgue, Tingwei Zhang
Cc: coresight, linux-arm-kernel, linux-kernel, linux-stm32, Jie Gan
Found a clock imbalance issue when remove the CTCU module.
coresight_get_enable_clocks() enables the programming clock and the
optional AT clock through devm_clk_get_optional_enabled(), which also
registers a devm action to call clk_disable_unprepare() when the driver
detaches.
After probe, pm_runtime_put() allows the device to suspend and the
runtime suspend callback disables the same clocks. During remove the
device is left runtime suspended, so pm_runtime_disable() freezes it
with the clocks already disabled. The devm cleanup that runs afterwards
calls clk_disable_unprepare() a second time, underflowing the clock
enable refcount.
Resume the device with pm_runtime_get_sync() before pm_runtime_disable()
so the clocks are enabled again and balance the devm-managed disable.
Calltrace:
[ 194.074015] ------------[ cut here ]------------
[ 194.078779] qdss already disabled
[ 194.082210] WARNING: drivers/clk/clk.c:1188 at clk_core_disable+0x238/0x240, CPU#4: rmmod/508
[ 194.090976] Modules linked in: coresight_ctcu(-) snd_soc_hdmi_codec snd_soc_core snd_compress snd_pcm_dmaengine snd_pcm snd_timer snd soundcore 8021q garp mrp phy_qcom_edp stp af_alg llc anx7625 typec pci_pwrctrl_pwrseq hci_uart qcom_iris v4l2_mem2mem btqca btbcm qcom_pon videobuf2_dma_contig rtc_pm8xxx nvmem_qcom_spmi_sdam qcom_spmi_temp_alarm videobuf2_memops qrtr videobuf2_v4l2 bluetooth msm qcom_stats pwrseq_qcom_wcn ubwc_config videodev ocmem ecdh_generic drm_gpuvm videobuf2_common drm_exec gpu_sched qcom_q6v5_pas kpp marvell videocc_sa8775p camcc_sa8775p ecc drm_dp_aux_bus dispcc0_sa8775p spi_geni_qcom i2c_qcom_geni llcc_qcom mc qcom_refgen_regulator phy_qcom_snps_femto_v2 phy_qcom_qmp_usb icc_bwmon phy_qcom_sgmii_eth dwmac_qcom_ethqos qcom_pil_info gpucc_sa8775p qcom_q6v5 stmmac_platform stmmac ufs_qcom qcom_sysmon drm_display_helper qcom_common pcs_xpcs phylink cec qcom_glink_smem qcrypto drm_client_lib mdt_loader dispcc1_sa8775p qmi_helpers phy_qcom_qmp_ufs libdes qcom_ice display_connector phy_qcom_qmp_pcie
[ 194.091130] qcom_wdt qcomtee nvmem_reboot_mode icc_osm_l3 qcom_rng drm_kms_helper cfg80211 rfkill socinfo fuse drm backlight stm_p_basic
[ 194.196124] CPU: 4 UID: 0 PID: 508 Comm: rmmod Not tainted 7.1.0-next-20260623-00008-ga4671328ba36 #831 PREEMPT
[ 194.206566] Hardware name: Qualcomm SA8775P Ride (DT)
[ 194.211771] pstate: 604000c5 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 194.218938] pc : clk_core_disable+0x238/0x240
[ 194.223426] lr : clk_core_disable+0x238/0x240
[ 194.227908] sp : ffff8000889fbb40
[ 194.231327] x29: ffff8000889fbb40 x28: ffff0000972eb580 x27: 0000000000000000
[ 194.238662] x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000
[ 194.245995] x23: ffffd181d3de4620 x22: ffff8000889fbc28 x21: ffff000082382810
[ 194.253322] x20: ffff000082334a00 x19: ffff000082334a00 x18: 0000000000000006
[ 194.260658] x17: ffffd181d293fb18 x16: ffffd181d295c3c8 x15: ffff8000889fb550
[ 194.267991] x14: 0000000000000000 x13: ffffd181d4eea620 x12: 00000000000004cf
[ 194.275346] x11: 0000000000000e6d x10: ffffd181d4f42620 x9 : ffffd181d4eea620
[ 194.282676] x8 : 3fffffffffffefff x7 : ffffd181d4f42620 x6 : bffffffffffff000
[ 194.290007] x5 : ffff000ead974248 x4 : 0000000000000000 x3 : ffff2e8cd9611000
[ 194.297338] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff0000972eb580
[ 194.304671] Call trace:
[ 194.307199] clk_core_disable+0x238/0x240 (P)
[ 194.311687] clk_disable+0x30/0x4c
[ 194.315192] clk_disable_unprepare+0x18/0x30
[ 194.319596] devm_clk_release+0x24/0x3c
[ 194.323578] dr_node_release+0x1c/0x28
[ 194.327466] release_nodes+0x5c/0x90
[ 194.331147] devres_release_all+0x90/0x104
[ 194.335364] device_unbind_cleanup+0x2c/0x84
[ 194.339762] device_release_driver_internal+0x200/0x23c
[ 194.345153] driver_detach+0x4c/0x94
[ 194.348835] bus_remove_driver+0x6c/0xbc
[ 194.352872] driver_unregister+0x30/0x60
[ 194.356914] platform_driver_unregister+0x14/0x20
[ 194.361753] ctcu_driver_exit+0x18/0xdf8 [coresight_ctcu]
[ 194.367308] __arm64_sys_delete_module+0x1bc/0x298
[ 194.372240] invoke_syscall+0x54/0x10c
[ 194.376114] el0_svc_common.constprop.0+0xc0/0xe0
[ 194.380956] do_el0_svc+0x1c/0x28
[ 194.384374] el0_svc+0x54/0x3a0
[ 194.387626] el0t_64_sync_handler+0xa0/0xe4
[ 194.391951] el0t_64_sync+0x198/0x19c
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
Jie Gan (2):
coresight: Fix clock refcount imbalance on platform remove
coresight: tnoc: Fix clock refcount imbalance on platform remove
drivers/hwtracing/coresight/coresight-catu.c | 1 +
drivers/hwtracing/coresight/coresight-cpu-debug.c | 1 +
drivers/hwtracing/coresight/coresight-ctcu-core.c | 1 +
drivers/hwtracing/coresight/coresight-etm4x-core.c | 1 +
drivers/hwtracing/coresight/coresight-funnel.c | 1 +
drivers/hwtracing/coresight/coresight-replicator.c | 1 +
drivers/hwtracing/coresight/coresight-stm.c | 1 +
drivers/hwtracing/coresight/coresight-tmc-core.c | 1 +
drivers/hwtracing/coresight/coresight-tnoc.c | 1 +
drivers/hwtracing/coresight/coresight-tpiu.c | 1 +
10 files changed, 10 insertions(+)
---
base-commit: be5c93fa674f0fc3c8f359c2143abce6bbb422e6
change-id: 20260701-fix-clock-refcount-unbalance-e7c467136a86
Best regards,
--
Jie Gan <jie.gan@oss.qualcomm.com>
^ permalink raw reply
* [PATCH 1/2] coresight: Fix clock refcount imbalance on platform remove
From: Jie Gan @ 2026-07-01 6:05 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin, Anshuman Khandual, Yeoreum Yun,
Yuanfang Zhang, Maxime Coquelin, Alexandre Torgue, Tingwei Zhang
Cc: coresight, linux-arm-kernel, linux-kernel, linux-stm32, Jie Gan
In-Reply-To: <20260701-fix-clock-refcount-unbalance-v1-0-321dc63c1f90@oss.qualcomm.com>
coresight_get_enable_clocks() enables the programming clock and the
optional AT clock through devm_clk_get_optional_enabled(), which also
registers a devm action to call clk_disable_unprepare() when the driver
detaches.
After probe, pm_runtime_put() allows the device to suspend and the
runtime suspend callback disables the same clocks. During remove the
device is left runtime suspended, so pm_runtime_disable() freezes it
with the clocks already disabled. The devm cleanup that runs afterwards
calls clk_disable_unprepare() a second time, underflowing the clock
enable refcount.
Resume the device with pm_runtime_get_sync() before pm_runtime_disable()
so the clocks are enabled again and balance the devm-managed disable.
This affects all CoreSight platform drivers that obtain their clocks
through coresight_get_enable_clocks(): catu, cpu-debug, ctcu, etm4x,
funnel, replicator, stm, tmc and tpiu.
Fixes: 1abc1b212eff ("coresight: Appropriately disable programming clocks")
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-catu.c | 1 +
drivers/hwtracing/coresight/coresight-cpu-debug.c | 1 +
drivers/hwtracing/coresight/coresight-ctcu-core.c | 1 +
drivers/hwtracing/coresight/coresight-etm4x-core.c | 1 +
drivers/hwtracing/coresight/coresight-funnel.c | 1 +
drivers/hwtracing/coresight/coresight-replicator.c | 1 +
drivers/hwtracing/coresight/coresight-stm.c | 1 +
drivers/hwtracing/coresight/coresight-tmc-core.c | 1 +
drivers/hwtracing/coresight/coresight-tpiu.c | 1 +
9 files changed, 9 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c
index ad8dafea7d2f..f7e501e6cbd7 100644
--- a/drivers/hwtracing/coresight/coresight-catu.c
+++ b/drivers/hwtracing/coresight/coresight-catu.c
@@ -647,6 +647,7 @@ static void catu_platform_remove(struct platform_device *pdev)
return;
__catu_remove(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c
index 3a806c1d50ea..f7efae5b5ce5 100644
--- a/drivers/hwtracing/coresight/coresight-cpu-debug.c
+++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c
@@ -711,6 +711,7 @@ static void debug_platform_remove(struct platform_device *pdev)
return;
__debug_remove(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c
index 9043cad42f01..9b6da29d9735 100644
--- a/drivers/hwtracing/coresight/coresight-ctcu-core.c
+++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c
@@ -266,6 +266,7 @@ static void ctcu_platform_remove(struct platform_device *pdev)
return;
ctcu_remove(pdev);
+ pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index 14bb31bd6a0b..147761024c0f 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -2416,6 +2416,7 @@ static void etm4_remove_platform_dev(struct platform_device *pdev)
if (drvdata)
etm4_remove_dev(drvdata);
+ pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c
index 0abc11f0690c..4c5b94640e6a 100644
--- a/drivers/hwtracing/coresight/coresight-funnel.c
+++ b/drivers/hwtracing/coresight/coresight-funnel.c
@@ -334,6 +334,7 @@ static void funnel_platform_remove(struct platform_device *pdev)
return;
funnel_remove(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c
index 2f382de357ee..2d765f1f73b3 100644
--- a/drivers/hwtracing/coresight/coresight-replicator.c
+++ b/drivers/hwtracing/coresight/coresight-replicator.c
@@ -313,6 +313,7 @@ static void replicator_platform_remove(struct platform_device *pdev)
return;
replicator_remove(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index 4e860519a73f..a653f1eebeca 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -1026,6 +1026,7 @@ static void stm_platform_remove(struct platform_device *pdev)
return;
__stm_remove(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
index bc5a133ada3e..c9cf486873e2 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-core.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
@@ -989,6 +989,7 @@ static void tmc_platform_remove(struct platform_device *pdev)
return;
__tmc_remove(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
index 7b029d2eb389..72ac93749be1 100644
--- a/drivers/hwtracing/coresight/coresight-tpiu.c
+++ b/drivers/hwtracing/coresight/coresight-tpiu.c
@@ -286,6 +286,7 @@ static void tpiu_platform_remove(struct platform_device *pdev)
return;
__tpiu_remove(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
--
2.34.1
^ permalink raw reply related
* [PATCH 2/2] coresight: tnoc: Fix clock refcount imbalance on platform remove
From: Jie Gan @ 2026-07-01 6:05 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin, Anshuman Khandual, Yeoreum Yun,
Yuanfang Zhang, Maxime Coquelin, Alexandre Torgue, Tingwei Zhang
Cc: coresight, linux-arm-kernel, linux-kernel, linux-stm32, Jie Gan
In-Reply-To: <20260701-fix-clock-refcount-unbalance-v1-0-321dc63c1f90@oss.qualcomm.com>
coresight_get_enable_clocks() enables the programming clock through
devm_clk_get_optional_enabled(), which also registers a devm action to
call clk_disable_unprepare() when the driver detaches.
After probe, pm_runtime_put() allows the device to suspend and the
runtime suspend callback disables the same clock. During remove the
device is left runtime suspended, so pm_runtime_disable() freezes it
with the clock already disabled. The devm cleanup that runs afterwards
calls clk_disable_unprepare() a second time, underflowing the clock
enable refcount.
Resume the device with pm_runtime_get_sync() before pm_runtime_disable()
so the clock is enabled again and balances the devm-managed disable.
Fixes: 5799dee92dc2 ("coresight-tnoc: add platform driver to support Interconnect TNOC")
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-tnoc.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c
index 9e8de4323d28..a4ac0eb02248 100644
--- a/drivers/hwtracing/coresight/coresight-tnoc.c
+++ b/drivers/hwtracing/coresight/coresight-tnoc.c
@@ -300,6 +300,7 @@ static void itnoc_remove(struct platform_device *pdev)
struct trace_noc_drvdata *drvdata = platform_get_drvdata(pdev);
coresight_unregister(drvdata->csdev);
+ pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v14 0/7] Provide support for Trigger Generation Unit
From: Songwei.Chai @ 2026-07-01 6:04 UTC (permalink / raw)
To: Suzuki K Poulose, Greg KH
Cc: andersson, alexander.shishkin, mike.leach, konrad.dybcio,
james.clark, krzk+dt, conor+dt, linux-kernel, linux-arm-kernel,
linux-arm-msm, coresight, devicetree
In-Reply-To: <e7d83356-c9cc-4324-a65e-fda3bd27af76@oss.qualcomm.com>
On 6/30/2026 3:59 PM, Songwei.Chai wrote:
>
>
> On 6/29/2026 6:44 PM, Suzuki K Poulose wrote:
>> Hello,
>>
>> On 29/06/2026 11:17, Songwei.Chai wrote:
>>>
>>> On 6/29/2026 12:22 PM, Greg KH wrote:
>>>> On Mon, Jun 29, 2026 at 11:03:33AM +0800, Songwei.Chai wrote:
>>>>> Hi Greg & Alexander,
>>>>>
>>>>> Apologies for interrupting again.
>>>>>
>>>>> As the TGU hardware plays an important role in Qualcomm tracing
>>>>> design, I
>>>>> would greatly appreciate it if you could kindly take some time to
>>>>> review
>>>>> this at your earliest convenience.
>>>> The merge window _just_ closed, please give us a chance to catch up.
>>>>
>>>> Also, why us? Surely you have other reviewers for this code, right?
>>>
>>> Hi Greg,
>>>
>>> Understood, thanks for letting us know.
>>>
>>> Regarding your question: since this introduces a new
>>> drivers/hwtracing/ qcom directory, there is no existing maintainer
>>> for it.
>>> Given your scope (and Alexander's), we believe you are the most
>>> relevant reviewers.
>>>
>>> The reason for creating the qcom directory is as follows:
>>>
>>> /We previously tried to upstream this driver under
>>> drivers/hwtracing/ coresight,/
>>> /but it was not accepted as it is considered Qualcomm-specific and
>>> not tightly/
>>> /coupled with the CoreSight subsystem. Based on this feedback, we are
>>
>> Some clarification here: This device is not CoreSight so we denied
>> keeping this under drivers/hwtracing/coresight/ - Not because it is
>> Qualcomm specific. We have TPDM, TPDA, TnoC devices under the coresight
>> subsystem, which are all Qualcomm specific for e.g.
>>
>> That said, there are other drivers in drivers/hwtracing/ which I usually
>> merge and push to Greg, after some reviews/acks from the respective
>> people (e.g., PTT HiSilicon PCIe Tune and Trace).
>>
>> But, your proposal was that there were other maintainers for your new
>> subtree and you were going to push this via ,linux-arm-msm ? to which I
>> didn't have any objections.
>>
>> That said, I am fine with pushing this to Greg via the CoreSight pull
>> requests (similar to Hisilicon PTT driver), but would need someone to
>> Maintain/Review the driver (with entries in MAINTAINERS, similar to
>> PTT).
>>
>>
>> Thoughts ?
>
> Hi Suzuki,
>
> Thank you for your constructive feedback in helping us move this patch
> forward.
> As the owner of this driver, together with Jie Gan (who has extensive
> review experience), we will be responsible for the maintenance and
> review going forward.
> I will also include the MAINTAINERS update in the next TGU release.
>
> Please feel free to share any further comments or suggestions.
>
> Thanks,
> Songwei
>
Hi Suzuki,
In the latest TGU Patch series (V15), I have also added you as a
maintainer so that you will be notified of any new drivers under
drivers/hwtracing/qcom.
The review responsibilities will be handled by Jie and myself.
Thanks,
Songwei
>>
>> Kind regards
>> Suzuki
>>
>>
>>
>>> exploring/
>>> /a dedicated drivers/hwtracing/qcom directory, similar to intel_th,
>>> to better/
>>> /support this and future Qualcomm hwtracing drivers./
>>>
>>> More details can be found in “[PATCH v14 0/7] -- Why we are
>>> proposing this”.
>>>
>>> Thanks,
>>> Songwei
>>>
>>>>
>>>> thanks,
>>>>
>>>> greg k-h
>>
^ permalink raw reply
* Re: [PATCH v2 0/5] netfilter: nf_flow_table_path: L2 bridge offload
From: Daniel Pawlik @ 2026-07-01 6:11 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: netfilter-devel, netdev, bridge, coreteam, linux-mediatek,
linux-arm-kernel
In-Reply-To: <akOCJI-2kAAwOQzz@chamomile>
Hi Florian, Pablo,
I'll leave it up to you - if `br_netfilter` isn't the right approach
in this case, then we can drop that series.
Before your reply, I wasn't familiar with Eric Woudstra's
"bridge-fastpath" series - thanks for the tip.
I'll take a look at it and try to build on those patches.
Thanks, and best regards,
Dan
wt., 30 cze 2026 o 10:45 Pablo Neira Ayuso <pablo@netfilter.org> napisał(a):
>
> Hi,
>
> On Tue, Jun 30, 2026 at 08:57:30AM +0200, Daniel Pawlik wrote:
> > This series adds L2 bridge offload support to nft_flow_offload, allowing
> > bridged IPv4/IPv6 flows to be accelerated by the flowtable fast path
> > without requiring L3 routing.
> >
> > Background
> > ----------
> > Hardware flow offload engines (e.g. MediaTek PPE) can accelerate bridged
> > traffic but require that nft_flow_offload detect and handle bridged flows
> > differently from routed ones: no routing table lookup, MAC addresses from
> > the Ethernet header, and VLAN context pre-populated from the bridge port.
> >
> > v2: Fix missing Returns: tags in kernel-doc comments for the three new
> > bridge helpers (br_fdb_has_forwarding_entry_rcu,
> > br_vlan_get_offload_info_rcu, br_vlan_is_enabled_rcu).
> >
> > Patches
> > -------
> > 1/5 net: export __dev_fill_forward_path
> > Refactors dev_fill_forward_path() to expose __dev_fill_forward_path()
> > which accepts a caller-supplied net_device_path_ctx, needed to
> > pre-populate VLAN state before the forward path walk.
> >
> > 2/5 net: bridge: add flow offload helpers
> > Adds br_fdb_has_forwarding_entry_rcu(), br_vlan_get_offload_info_rcu()
> > and br_vlan_is_enabled_rcu() to expose bridge state to nft_flow_offload
> > without requiring inclusion of net/bridge/br_private.h.
> >
> > 3/5 netfilter: nf_flow_table_path: add L2 bridge offload
> > Core of the series. Adds nft_flow_offload_is_bridging() detection,
> > nft_flow_route_bridging() which avoids nf_route() (fails for
> > bridged-only subnets), MAC/VLAN pre-population for bridged flows,
> > and a dst leak fix. nft_flow_route() becomes a thin dispatcher.
> >
> > 4/5 netfilter: nf_flow_table_path: handle DEV_PATH_MTK_WDMA in path info
> > Fixes zero-source-MAC in PPE entries when a bridged flow traverses
> > MT7996/MT7915 WiFi WDMA hardware.
> >
> > 5/5 netfilter: nf_flow_table_path: add VLAN passthrough support
> > Records VLAN encap info for passthrough-mode bridge ports so hardware
> > offload entries include the correct VLAN tag.
> >
> > Rebase note
> > -----------
> > Originally developed against OpenWrt pending-6.18 patches by Ryan Chen
> > <rchen14b@gmail.com> and Bo-Cun Chen <bc-bocun.chen@mediatek.com>.
> > Rebased to current upstream: path discovery infrastructure moved to
> > nf_flow_table_path.c in commit 93d7a7ed0734 ("netfilter: flowtable: move
> > path discovery infrastructure to its own file"), so all netfilter changes
> > now land in that file rather than nft_flow_offload.c.
> >
> > How to enable bridge offload
> > -----------------------------
> > 1. Load kmod-br-netfilter so that bridged IP traffic traverses the
> > netfilter forward chain.
> >
> > 2. Enable netfilter hooks on the bridge:
> > echo 1 > /sys/class/net/<br>/bridge/nf_call_iptables
> > echo 1 > /sys/class/net/<br>/bridge/nf_call_ip6tables
>
> This requires br_netfilter which is a no go.
>
> Sorry, but we should really target at the native nf_conntrack_bridge
> support.
>
> > 3. Register bridge member interfaces in the nft flowtable:
> > table inet filter {
> > flowtable f {
> > hook ingress priority filter
> > devices = { eth0, wlan0 }
> > }
> > chain forward {
> > type filter hook forward priority filter
> > meta l4proto { tcp, udp } flow add @f
> > }
> > }
>
> Yes, but br_netfilter makes no sense for nftables.
>
> br_netfilter was made to fill gap at the time ebtables was lagging a
> lot behind iptables in terms of features. And getting ebtables on pair
> with iptables in functionality was not feasible either, because it
> required many new extensions that were specific of the bridge family,
> which probably was not a big deal, but it also required to get
> the ebtables command line tool on pair with iptables userspace, which
> has received more development attention/effort that the bridge tool.
>
> All of this does not stand true anymore with nftables, where the
> bridge family capabilities are at pair with the inet families.
>
> I am looking now at the native flowtable bridge support, I will get
> back to you with updates.
^ permalink raw reply
* [PATCH] ASoC: mediatek: mt8173-rt5650: tidyup error message
From: Kuninori Morimoto @ 2026-07-01 6:17 UTC (permalink / raw)
To: AngeloGioacchino Del Regno, Jaroslav Kysela, Liam Girdwood,
linux-arm-kernel, linux-sound, Mark Brown, Matthias Brugger,
Takashi Iwai
mt8173_rt5650_dev_probe() has strange error message
=> ret = device_property_read_u32(...);
^^^^^^^^^^^^^^^^^^^^^^^^
if (ret)
=> dev_err(... "%s snd_soc_register_card fail %d\n", ...);
^^^^^^^^^^^^^^^^^^^^^
It should be "device_property_read_u32() fail". Fix it.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
sound/soc/mediatek/mt8173/mt8173-rt5650.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
index 3d6d7bc05b872..8c5096482520a 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -315,7 +315,7 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
&mt8173_rt5650_priv.pll_from);
if (ret) {
dev_err(&pdev->dev,
- "%s snd_soc_register_card fail %d\n",
+ "%s device_property_read_u32() fail %d\n",
__func__, ret);
}
}
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v3 0/8] treewide: remove unnecessary invalid range checks in memblock iteration loops
From: Mike Rapoport @ 2026-07-01 6:22 UTC (permalink / raw)
To: Albert Ou, Andrew Morton, Andrey Ryabinin, Catalin Marinas,
Huacai Chen, Madhavan Srinivasan, Michael Ellerman, Muchun Song,
Oscar Salvador, Palmer Dabbelt, Paul Walmsley, Russell King,
Will Deacon, Sang-Heon Jeon
Cc: linux-mm, Alexander Potapenko, Alexandre Ghiti, Andrey Konovalov,
Christophe Leroy (CS GROUP), David Hildenbrand, Dmitry Vyukov,
kasan-dev, linux-arm-kernel, linux-kernel, linuxppc-dev,
linux-riscv, loongarch, Nicholas Piggin, Vincenzo Frascino,
WANG Xuerui
In-Reply-To: <20260630150413.1718632-1-ekffu200098@gmail.com>
On Wed, 01 Jul 2026 00:04:05 +0900, Sang-Heon Jeon wrote:
> treewide: remove unnecessary invalid range checks in memblock iteration loops
>
> The memblock API guarantees that for_each_mem_range() and
> for_each_mem_pfn_range() never return an invalid range, meaning start is
> always less than end.
>
> Several memblock callers still have unnecessary invalid range checks in
> their loop bodies, so remove them.
>
> [...]
Applied to range-checks branch of memblock.git tree, thanks!
[1/8] arm64: mm: remove unreachable invalid range check in kasan_init_shadow()
commit: f6e6c57f2e100813be6d5882060fef81cb6f32de
[2/8] LoongArch: remove unreachable invalid range check in kasan_init()
commit: 68d3b7d58237b8794d9343210d4c90b8381cfa96
[3/8] riscv: remove unreachable invalid range check in create_linear_mapping_page_table()
commit: 99e60ebe108817fd48791f2cb81f1e672ad8285b
[4/8] riscv: remove unreachable invalid range check in kasan_init()
commit: 8008f6995d0c0cf22e7b4d60e95fe14085c10f35
[5/8] ARM: remove unreachable invalid range check in kasan_init()
commit: 3db1a9ccf9290ea3fe01aadf3bd743ef7032b24a
[6/8] powerpc64/kasan: Remove unreachable invalid range check in kasan_init_phys_region()
commit: 21e95a4b6f34770571eeeaa6adb535efba1b7281
[7/8] mm: remove unnecessary empty range check in early_calculate_totalpages()
commit: c6af91e48594191f62ad3c1fcc8269b1eb539ef8
[8/8] mm/hugetlb: remove unnecessary empty range check in hugetlb_bootmem_set_nodes()
commit: 2db44dc8bdd3cfc933a3193724f4d38b03e35b80
tree: https://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock
branch: range-checks
--
Sincerely yours,
Mike.
^ permalink raw reply
* [RFC PATCH 0/2] Add NPAC driver to accel subsystem
From: Jaspinder Budhal @ 2026-07-01 6:25 UTC (permalink / raw)
To: ogabbay, corbet, skhan
Cc: dri-devel, linux-doc, linux-kernel, linux-arm-kernel, j-budhal,
srk, s-vadapalli
This series adds support for the Network Packet ACcelerator (NPAC)
client driver. NPAC is a hardware accelerator for offloading
packet processing tasks from the various hosts on TI's TDA5 family
of SoCs.
This is a minimal reference implementation to establish the framework
for NPAC acceleration support in the Linux kernel. Further details
are in patch 1 which adds documentation for the driver and describes
the system architecture, driver interface, and execution flows.
This RFC is submitted for getting feedback on the initial implementation.
Jaspinder Budhal (2):
Documentation: accel: Add NPAC client driver documentation
accel/npac: Add NPAC client driver
Documentation/accel/npac.rst | 142 ++++++++++++++++++++++
drivers/accel/Kconfig | 1 +
drivers/accel/Makefile | 1 +
drivers/accel/npac/Kconfig | 13 ++
drivers/accel/npac/Makefile | 3 +
drivers/accel/npac/npac_drv.c | 218 ++++++++++++++++++++++++++++++++++
6 files changed, 378 insertions(+)
create mode 100644 Documentation/accel/npac.rst
create mode 100644 drivers/accel/npac/Kconfig
create mode 100644 drivers/accel/npac/Makefile
create mode 100644 drivers/accel/npac/npac_drv.c
--
2.34.1
^ permalink raw reply
* [RFC PATCH 1/2] Documentation: accel: Add NPAC client driver documentation
From: Jaspinder Budhal @ 2026-07-01 6:25 UTC (permalink / raw)
To: ogabbay, corbet, skhan
Cc: dri-devel, linux-doc, linux-kernel, linux-arm-kernel, j-budhal,
srk, s-vadapalli
In-Reply-To: <20260701062505.1091840-1-j-budhal@ti.com>
NPAC (Network Packet ACcelerator) is a hardware accelerator present on
TI's TDA5 family of SoCs, designed to offload network packet processing
tasks from various hosts. It can be used to implement various packet
processing use cases such as ACL (Access Control List) rule enforcement,
reducing host CPU load.
This is an RFC patch for getting feedback on the initial implementation.
See the TDA54 SoC datasheet for hardware details:
https://www.ti.com/lit/ds/symlink/tda54-q1.pdf
Signed-off-by: Jaspinder Budhal <j-budhal@ti.com>
---
Documentation/accel/npac.rst | 142 +++++++++++++++++++++++++++++++++++
1 file changed, 142 insertions(+)
create mode 100644 Documentation/accel/npac.rst
diff --git a/Documentation/accel/npac.rst b/Documentation/accel/npac.rst
new file mode 100644
index 000000000000..fba6ddd0cfd2
--- /dev/null
+++ b/Documentation/accel/npac.rst
@@ -0,0 +1,142 @@
+.. SPDX-License-Identifier: GPL-2.0-only OR MIT
+
+====================================================
+Texas Instruments NPAC (Network Packet ACcelerator)
+====================================================
+
+Overview
+========
+
+NPAC (Network Packet ACcelerator) is a hardware accelerator available
+on Texas Instruments SoCs in the TDA5 family that provides network packet
+processing acceleration [0]_.
+
+The NPAC Host Driver runs on a dedicated remote processor and manages
+NPAC hardware.
+
+The NPAC client driver provides offloading support for ethernet-related tasks
+and communicates with the NPAC Host Driver over RPMsg (Remote Processor
+Messaging).
+
+The driver exposes /dev/accel/accel* as the userspace interface for accelerator
+configuration and management operations.
+
+The NPAC Host Driver supports ACL (Access Control
+List) [1]_ rule offload. ACL configuration requests are sent by the client
+driver to the NPAC Host Driver.
+
+Communication Model
+-------------------
+
+The NPAC client driver registers with the RPMsg subsystem during driver
+initialization.
+
+When the NPAC host driver announces the NPAC RPMsg service endpoint, the
+RPMsg core matches the announced service against the registered NPAC client
+driver and invokes the driver probe callback.
+
+Offload command received from userspace are formatted into RPMsgs and sent
+to the NPAC Host Driver.
+
+NPAC Host Driver responses are received through RPMsg callbacks.
+
+ACL Rule Offload
+----------------
+
+ACL (Access Control List) rules are packet filtering policies that define how
+network traffic should be handled — for example, dropping packets from a
+blacklisted source IP or allowing traffic only from trusted MAC addresses.
+
+In the standard Linux networking stack, ACL enforcement is handled in software
+by the netfilter subsystem. Each packet is inspected against the configured
+ruleset on the host CPU. On embedded SoCs in the TDA5 family such as TDA54,
+the per-packet overhead of software-based ACL enforcement accumulates under
+high traffic rates, increasing CPU utilization and reducing the processing
+capacity available for other system functions.
+
+The NPAC client driver offloads rules from Linux to NPAC hardware.
+Once a rule is programmed, matching packets are handled entirely by NPAC,
+reducing CPU load.
+
+For example, a rule to drop all packets from a specific source IP or to
+allow packets from a trusted source is offloaded to NPAC. Instead of the
+host parsing each packet and applying the ACL rules, NPAC enforces the
+rule directly in hardware.
+
+Userspace Interface
+-------------------
+
+The driver exposes /dev/accel/accel* as the userspace control interface for ACL
+rule management operations.
+
+The interface uses IOCTL-based request handling for accelerator configuration
+and rule management operations.
+
+IOCTL Commands
+~~~~~~~~~~~~~~
+
+DRM_IOCTL_NPAC_ACL_ADD_RULE
+
+ This IOCTL allows userspace to add an ACL rule to the accelerator. The
+ call will forward the rule to the NPAC host driver for hardware programming.
+ The NPAC Host Driver is responsible for validating the rule and applying it to
+ the NPAC hardware.
+
+DRM_IOCTL_NPAC_ACL_DELETE_RULE
+
+ This IOCTL allows userspace to remove an ACL rule from the accelerator.
+ The call will forward the deletion request to the NPAC host driver, which
+ will remove the rule from the NPAC hardware.
+
+Driver Lifecycle
+----------------
+
+The driver lifecycle is coordinated through the RPMsg framework.
+
+Probe Flow
+~~~~~~~~~~
+
+1. The NPAC client driver registers with the RPMsg subsystem
+
+2. NPAC host driver initializes on the remote processor
+
+3. NPAC Host Driver announces its RPMsg service endpoint
+
+4. The RPMsg subsystem in Linux matches the announced service against
+ the registered NPAC client driver
+
+5. The RPMsg subsystem invokes the driver probe callback
+
+6. Driver communication resources are initialized
+
+7. /dev/accel/accel* becomes available to userspace
+
+Request Flow
+~~~~~~~~~~~~
+
+1. Userspace submits an ACL configuration request through /dev/accel/accel*
+
+2. The request is forwarded to the NPAC host driver using RPMsg
+
+3. The NPAC Host Driver validates the request and performs the required hardware
+ programming operations
+
+4. NPAC Host Driver responses are received by the Linux driver through the RPMsg
+ callback.
+
+RPMsg Integration
+-----------------
+
+The driver integrates with the Linux RPMsg subsystem for communication with
+the NPAC host driver processor.
+
+RPMsg is used as the transport mechanism for:
+
+- Accelerator configuration requests
+- NPAC Host Driver responses
+
+References
+----------
+
+.. [0] https://www.ti.com/lit/ds/symlink/tda54-q1.pdf
+.. [1] https://en.wikipedia.org/wiki/Access-control_list#Networking_ACLs
--
2.34.1
^ permalink raw reply related
* [RFC PATCH 2/2] accel/npac: Add NPAC client driver
From: Jaspinder Budhal @ 2026-07-01 6:25 UTC (permalink / raw)
To: ogabbay, corbet, skhan
Cc: dri-devel, linux-doc, linux-kernel, linux-arm-kernel, j-budhal,
srk, s-vadapalli
In-Reply-To: <20260701062505.1091840-1-j-budhal@ti.com>
Add the Linux client driver for the Network Packet ACcelerator (NPAC).
The driver communicates with the NPAC host driver via RPMsg,
allowing offloading of ethernet-related packet processing tasks.
The driver provides the basic framework for receiving offload commands
via IOCTL and communicating with the NPAC hardware accelerator.
Signed-off-by: Jaspinder Budhal <j-budhal@ti.com>
---
drivers/accel/Kconfig | 1 +
drivers/accel/Makefile | 1 +
drivers/accel/npac/Kconfig | 13 ++
drivers/accel/npac/Makefile | 3 +
drivers/accel/npac/npac_drv.c | 218 ++++++++++++++++++++++++++++++++++
5 files changed, 236 insertions(+)
create mode 100644 drivers/accel/npac/Kconfig
create mode 100644 drivers/accel/npac/Makefile
create mode 100644 drivers/accel/npac/npac_drv.c
diff --git a/drivers/accel/Kconfig b/drivers/accel/Kconfig
index bdf48ccafcf2..a6d8c1ebfcf1 100644
--- a/drivers/accel/Kconfig
+++ b/drivers/accel/Kconfig
@@ -28,6 +28,7 @@ source "drivers/accel/amdxdna/Kconfig"
source "drivers/accel/ethosu/Kconfig"
source "drivers/accel/habanalabs/Kconfig"
source "drivers/accel/ivpu/Kconfig"
+source "drivers/accel/npac/Kconfig"
source "drivers/accel/qaic/Kconfig"
source "drivers/accel/rocket/Kconfig"
diff --git a/drivers/accel/Makefile b/drivers/accel/Makefile
index 1d3a7251b950..0df7b9f2ae18 100644
--- a/drivers/accel/Makefile
+++ b/drivers/accel/Makefile
@@ -4,5 +4,6 @@ obj-$(CONFIG_DRM_ACCEL_AMDXDNA) += amdxdna/
obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) += ethosu/
obj-$(CONFIG_DRM_ACCEL_HABANALABS) += habanalabs/
obj-$(CONFIG_DRM_ACCEL_IVPU) += ivpu/
+obj-$(CONFIG_DRM_ACCEL_NPAC_CLIENT) += npac/
obj-$(CONFIG_DRM_ACCEL_QAIC) += qaic/
obj-$(CONFIG_DRM_ACCEL_ROCKET) += rocket/
\ No newline at end of file
diff --git a/drivers/accel/npac/Kconfig b/drivers/accel/npac/Kconfig
new file mode 100644
index 000000000000..a9543ac2c7aa
--- /dev/null
+++ b/drivers/accel/npac/Kconfig
@@ -0,0 +1,13 @@
+config DRM_ACCEL_NPAC_CLIENT
+ tristate "NPAC Client Driver"
+ depends on DRM_ACCEL
+ depends on RPMSG
+ help
+ This driver is the NPAC (Network Packet ACcelerator) client,
+ communicating with the NPAC host driver running on a remote
+ processor over RPMsg. The driver registers with the DRM accelerator
+ framework and exposes /dev/accel/accel* as the userspace interface
+ for ACL rule offload operations.
+ If you have a device with NPAC and wish to use it with this
+ driver, say M to compile this driver as a module, or Y to
+ compile it into the kernel.
diff --git a/drivers/accel/npac/Makefile b/drivers/accel/npac/Makefile
new file mode 100644
index 000000000000..89f4773d7041
--- /dev/null
+++ b/drivers/accel/npac/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_DRM_ACCEL_NPAC_CLIENT) += npac_drv.o
diff --git a/drivers/accel/npac/npac_drv.c b/drivers/accel/npac/npac_drv.c
new file mode 100644
index 000000000000..7516382ba74b
--- /dev/null
+++ b/drivers/accel/npac/npac_drv.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include <drm/drm_accel.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_ioctl.h>
+#include <linux/completion.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rpmsg.h>
+
+#define NPAC_RPMSG_SERVICE_NAME "ti.npac"
+#define NPAC_ACK_TIMEOUT_MS 1000
+
+/**
+ * enum npac_acl_cmd - IOCTL command numbers for NPAC ACL operations.
+ */
+enum npac_acl_cmd {
+ /** @NPAC_ACL_ADD_RULE: Add an ACL rule. */
+ NPAC_ACL_ADD_RULE,
+
+ /** @NPAC_ACL_DELETE_RULE: Delete an ACL rule. */
+ NPAC_ACL_DELETE_RULE,
+};
+
+/**
+ * enum npac_acl_match_type - Match field selector for an ACL rule.
+ */
+enum npac_acl_match_type {
+ /** @NPAC_ACL_MATCH_SRC_IPV4: Match on source IPv4 address. */
+ NPAC_ACL_MATCH_SRC_IPV4 = 1,
+
+ /** @NPAC_ACL_MATCH_DST_IPV4: Match on destination IPv4 address. */
+ NPAC_ACL_MATCH_DST_IPV4,
+
+ /** @NPAC_ACL_MATCH_SRC_MAC: Match on source MAC address. */
+ NPAC_ACL_MATCH_SRC_MAC,
+
+ /** @NPAC_ACL_MATCH_DST_MAC: Match on destination MAC address. */
+ NPAC_ACL_MATCH_DST_MAC,
+};
+
+/**
+ * enum npac_acl_verdict - Action to apply when an ACL rule matches.
+ */
+enum npac_acl_verdict {
+ /** @NPAC_ACL_VERDICT_ALLOW: Accept the matched packet. */
+ NPAC_ACL_VERDICT_ALLOW = 1,
+
+ /** @NPAC_ACL_VERDICT_DROP: Drop the matched packet. */
+ NPAC_ACL_VERDICT_DROP,
+};
+
+/* Sized for the largest match field: a 6-byte MAC address. */
+#define NPAC_ACL_MATCH_VALUE_MAX 6
+
+struct npac_acl_offload_rule {
+ __u32 match_type;
+ __u32 verdict;
+ __u8 match_value[NPAC_ACL_MATCH_VALUE_MAX];
+};
+
+#define DRM_IOCTL_NPAC_ACL_ADD_RULE \
+ DRM_IOWR(DRM_COMMAND_BASE + NPAC_ACL_ADD_RULE, struct npac_acl_offload_rule)
+
+#define DRM_IOCTL_NPAC_ACL_DELETE_RULE \
+ DRM_IOWR(DRM_COMMAND_BASE + NPAC_ACL_DELETE_RULE, struct npac_acl_offload_rule)
+
+struct npac_device {
+ struct drm_device drm;
+ struct rpmsg_device *rpdev;
+ struct mutex req_lock; /* lock for request */
+ struct completion ack;
+};
+
+static inline struct npac_device *drm_to_npac_device(struct drm_device *dev)
+{
+ return container_of(dev, struct npac_device, drm);
+}
+
+static int npac_add_rule_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct npac_device *npac = drm_to_npac_device(dev);
+ struct npac_acl_offload_rule *rule = data;
+ int ret;
+
+ dev_dbg(dev->dev, "add rule ioctl received\n");
+
+ mutex_lock(&npac->req_lock);
+ reinit_completion(&npac->ack);
+ ret = rpmsg_send(npac->rpdev->ept, rule, sizeof(*rule));
+ if (ret) {
+ dev_err(dev->dev, "rpmsg_send failed: %d\n", ret);
+ mutex_unlock(&npac->req_lock);
+ return ret;
+ }
+ if (!wait_for_completion_timeout(&npac->ack,
+ msecs_to_jiffies(NPAC_ACK_TIMEOUT_MS))) {
+ dev_err(dev->dev, "ack timed out\n");
+ mutex_unlock(&npac->req_lock);
+ return -ETIMEDOUT;
+ }
+ mutex_unlock(&npac->req_lock);
+ return 0;
+}
+
+static int npac_delete_rule_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct npac_device *npac = drm_to_npac_device(dev);
+ struct npac_acl_offload_rule *rule = data;
+ int ret;
+
+ dev_dbg(dev->dev, "delete rule ioctl received\n");
+
+ mutex_lock(&npac->req_lock);
+ reinit_completion(&npac->ack);
+ ret = rpmsg_send(npac->rpdev->ept, rule, sizeof(*rule));
+ if (ret) {
+ dev_err(dev->dev, "rpmsg_send failed: %d\n", ret);
+ mutex_unlock(&npac->req_lock);
+ return ret;
+ }
+ if (!wait_for_completion_timeout(&npac->ack,
+ msecs_to_jiffies(NPAC_ACK_TIMEOUT_MS))) {
+ dev_err(dev->dev, "ack timed out\n");
+ mutex_unlock(&npac->req_lock);
+ return -ETIMEDOUT;
+ }
+ mutex_unlock(&npac->req_lock);
+ return 0;
+}
+
+static const struct drm_ioctl_desc npac_drm_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(NPAC_ACL_ADD_RULE, npac_add_rule_ioctl, DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF_DRV(NPAC_ACL_DELETE_RULE, npac_delete_rule_ioctl, DRM_ROOT_ONLY),
+};
+
+DEFINE_DRM_ACCEL_FOPS(npac_fops);
+
+static const struct drm_driver npac_drm_driver = {
+ .driver_features = DRIVER_COMPUTE_ACCEL,
+ .ioctls = npac_drm_ioctls,
+ .num_ioctls = ARRAY_SIZE(npac_drm_ioctls),
+ .fops = &npac_fops,
+ .name = "npac",
+ .desc = "NPAC Client Driver",
+};
+
+static int npac_cb(struct rpmsg_device *rpdev, void *data, int len,
+ void *priv, u32 src)
+{
+ struct npac_device *npac = dev_get_drvdata(&rpdev->dev);
+
+ dev_dbg(&rpdev->dev, "ack received\n");
+ complete(&npac->ack);
+ return 0;
+}
+
+static int npac_probe(struct rpmsg_device *rpdev)
+{
+ struct npac_device *npac;
+ int ret;
+
+ npac = devm_drm_dev_alloc(&rpdev->dev, &npac_drm_driver,
+ struct npac_device, drm);
+ if (IS_ERR(npac))
+ return PTR_ERR(npac);
+
+ npac->rpdev = rpdev;
+ mutex_init(&npac->req_lock);
+ init_completion(&npac->ack);
+ dev_set_drvdata(&rpdev->dev, npac);
+
+ ret = drm_dev_register(&npac->drm, 0);
+ if (ret) {
+ dev_err(&rpdev->dev, "Failed to register DRM accel device: %d\n", ret);
+ return ret;
+ }
+
+ dev_info(&rpdev->dev, "NPAC Client driver probed\n");
+ return 0;
+}
+
+static void npac_remove(struct rpmsg_device *rpdev)
+{
+ struct npac_device *npac = dev_get_drvdata(&rpdev->dev);
+
+ drm_dev_unplug(&npac->drm);
+
+ dev_info(&rpdev->dev, "NPAC Client Driver removed\n");
+}
+
+static struct rpmsg_device_id npac_id_table[] = {
+ { .name = NPAC_RPMSG_SERVICE_NAME },
+ {},
+};
+MODULE_DEVICE_TABLE(rpmsg, npac_id_table);
+
+static struct rpmsg_driver npac_driver = {
+ .drv.name = KBUILD_MODNAME,
+ .id_table = npac_id_table,
+ .probe = npac_probe,
+ .callback = npac_cb,
+ .remove = npac_remove,
+};
+
+module_rpmsg_driver(npac_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NPAC Client Driver");
+MODULE_AUTHOR("Jaspinder Budhal <j-budhal@ti.com>");
--
2.34.1
^ permalink raw reply related
* Re: [PULL] PCI: meson: Fix PERST# timing by asserting reset before LTSSM enable
From: Manivannan Sadhasivam @ 2026-07-01 6:31 UTC (permalink / raw)
To: gowtham
Cc: yue.wang, lpieralisi, kwilczynski, robh, bhelgaas, neil.armstrong,
khilman, jbrunet, martin.blumenstingl, linux-pci, linux-amlogic,
linux-arm-kernel, linux-kernel
In-Reply-To: <20260614095626.7afb7c04@slick.ferryfair.com>
On Sun, Jun 14, 2026 at 09:56:26AM +0530, gowtham wrote:
> The following changes since commit
> bb532bfaf7919c7c98caab81864e9ce2646e11e3:
>
> Linux 7.0.11 (2026-06-01 17:54:55 +0200)
>
> are available in the Git repository at:
>
> https://github.com/GowthamKudupudi/linux.git
> tags/meson-pcie-warm-reset-linux-7.0.y
>
> for you to fetch changes up to 852811b11795ee389ea6a953ed0db69b76722469:
>
> PCI: meson: Fix PERST# timing by asserting reset before LTSSM enable
> (2026-06-14 09:41:01 +0530)
>
Sorry, we don't pull patches from random trees. Please submit the patches to
linux-pci for review.
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH] net: airoha: fix MIB stats collection to be lossless
From: Aniket Negi @ 2026-07-01 6:38 UTC (permalink / raw)
To: Lorenzo Bianconi
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, linux-arm-kernel, linux-mediatek, netdev,
linux-kernel, aniket.negi
In-Reply-To: <akPQ7P1Dqb00oWa3@lore-desk>
Hi Lorenzo,
Thank you for the detailed review and suggestions!
> > + /* TX - 64-bit H+L registers: hw accumulates the total, read directly. =
> */
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
> > - dev->stats.tx_ok_pkts += ((u64)val << 32);
> > - val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
> > - dev->stats.tx_ok_pkts += val;
> > + dev->stats.tx_ok_pkts = (u64)val << 32;
>
> I guess it is more readable to store REG_FE_GDM_TX_OK_PKT_CNT_L() read in v=
> al
> here. Something like:
>
> val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
> dev->stats.tx_ok_pkts += val;
>
> This apply even to occurrence below
Agreed. I'll store CNT_L read in val first to improve readability.
> > + dev->stats.tx_ok_pkts += airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L=
> (port->id));
> > =20
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
> > - dev->stats.tx_ok_bytes += ((u64)val << 32);
> > - val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
> > - dev->stats.tx_ok_bytes += val;
> > + dev->stats.tx_ok_bytes = (u64)val << 32;
> > + dev->stats.tx_ok_bytes += airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT=
> _L(port->id));
> > =20
> > + /* TX - 32-bit registers: accumulate delta to handle wrap-around. */
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
> > - dev->stats.tx_drops += val;
> > + dev->stats.tx_drops += (u32)(val - dev->stats.hw_prev_stats.tx_drops);
> > + dev->stats.hw_prev_stats.tx_drops = val;
> > =20
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
> > - dev->stats.tx_broadcast += val;
> > + dev->stats.tx_broadcast += (u32)(val - dev->stats.hw_prev_stats.tx_br=
> oadcast);
> > + dev->stats.hw_prev_stats.tx_broadcast = val;
> > =20
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
> > - dev->stats.tx_multicast += val;
> > + dev->stats.tx_multicast += (u32)(val - dev->stats.hw_prev_stats.tx_mu=
> lticast);
> > + dev->stats.hw_prev_stats.tx_multicast = val;
> > =20
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
> > - dev->stats.tx_len[i] += val;
> > + dev->stats.tx_len[i] += (u32)(val - dev->stats.hw_prev_stats.tx_len[i=
> ]);
> > + dev->stats.hw_prev_stats.tx_len[i] = val;
> > =20
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
> > - dev->stats.tx_len[i] += ((u64)val << 32);
> > - val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
> > - dev->stats.tx_len[i++] += val;
> > + dev->stats.tx_len[i] += (u64)val << 32;
>
> Since now we do not reset MIB counters, this is wrong, you can't use "+="
You are absolutely right, since MIB counters are no longer cleared, using "+=" for E64 counter would cause double counting each iteration. This was missed in the patch, specifically for the case where runt count(32 bit) and E64 counter (64 bit) need to be combined in the same counter.
I'll fix this by using separate accumulator fields to "tx_runt_accum/rx_runt_accum" to track the runt deltas, then compute tx_len[i] as tx_len[i]= tx_runt_accum + E64_CNT (H+L).
> > val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
> > - dev->stats.rx_len[i] += val;
> > + dev->stats.rx_len[i] += (u32)(val - dev->stats.hw_prev_stats.rx_len[i=
> ]);
> > + dev->stats.hw_prev_stats.rx_len[i] = val;
> > =20
> > val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
> > - dev->stats.rx_len[i] += ((u64)val << 32);
> > - val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
> > - dev->stats.rx_len[i++] += val;
> > + dev->stats.rx_len[i] += (u64)val << 32;
>
> same here.
Acked. The same approach above will be applied to rx_len[i].
> > +
> > + struct {
> > + /* Previous HW register values for 32-bit counter delta tracking.
> > + * Storing the last seen value and accumulating (u32)(curr - prev)
> > + * in 64-bit software counter & handles wrap-around transparently
> > + * via unsigned arithmetic. These fields are never reported to
> > + * userspace.
> > + */
>
> can you please align the comment here?
Will fix the comment alignment.
>
> > + u32 tx_drops;
> > + u32 tx_broadcast;
> > + u32 tx_multicast;
> > + u32 tx_len[7];
> > + u32 rx_drops;
> > + u32 rx_broadcast;
> > + u32 rx_multicast;
> > + u32 rx_errors;
> > + u32 rx_crc_error;
> > + u32 rx_over_errors;
> > + u32 rx_fragment;
> > + u32 rx_jabber;
> > + u32 rx_len[7];
> > + } hw_prev_stats;
>
> Maybe something like "prev_val32" ?
>
> Will update the name of struct to hold prev counter from hw_pre_stats to prev_val32.
Good suggestion. However, since the struct hw_prev_stats now contains both u32 (previous register value) and u64 (runt accumulators) fields. I'll rename it to "prev_mib_state" to better reflect its dual purpose of storing previous register values for delta calculation and accumulators for combined counters.
Regards,
Aniket Negi
^ permalink raw reply
* Re: [PATCH 3/3] net: stmmac: dwmac-socfpga: Add mac-mode DT property support
From: Maxime Chevallier @ 2026-07-01 6:49 UTC (permalink / raw)
To: muhammad.nazim.amirul.nazle.asmade, dinguyen
Cc: rmk+kernel, krzk+dt, conor+dt, robh, davem, edumazet, kuba,
pabeni, andrew+netdev, devicetree, linux-arm-kernel, netdev,
linux-kernel
In-Reply-To: <20260630133108.27244-4-muhammad.nazim.amirul.nazle.asmade@altera.com>
Hi,
On 6/30/26 15:31, muhammad.nazim.amirul.nazle.asmade@altera.com wrote:
> From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
>
> Russell King's commit de696c63c1dc ("net: stmmac: socfpga: convert to
> use phy_interface") replaced mac_interface with phy_interface in
> socfpga_get_plat_phymode(), noting that no upstream DTS files set the
> "mac-mode" property, making the two values identical.
>
> The Agilex5 SoCDK TSN Config2 board is an exception: its gmac1 TSN
> port uses GMII internally in the MAC while the PHY-side interface is
> RGMII, so mac-mode and phy-mode differ. Without restoring mac_interface
> support, the MAC is configured with RGMII instead of GMII, causing
> connectivity failures on this board.
>
> Add socfpga_of_get_mac_mode() to read the optional "mac-mode" DT
> property and store it in a new mac_interface field. When the property
> is absent, mac_interface falls back to phy_interface, preserving
> the existing behaviour for all other boards.
After our discussions, indeed mac-mode seems to be the way to go, however
I have some remarks on how it's handled right now.
>
> Fixes: de696c63c1dc ("net: stmmac: socfpga: convert to use phy_interface")
> Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
> ---
> .../ethernet/stmicro/stmmac/dwmac-socfpga.c | 23 ++++++++++++++++++-
> 1 file changed, 22 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
> index 1d7f0a57d288..6a6837c4a414 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
> @@ -69,12 +69,30 @@ struct socfpga_dwmac {
> void __iomem *tse_pcs_base;
> void __iomem *sgmii_adapter_base;
> bool f2h_ptp_ref_clk;
> + phy_interface_t mac_interface;
> const struct socfpga_dwmac_ops *ops;
> };
>
> +static int socfpga_of_get_mac_mode(struct device_node *np)
> +{
> + const char *pm;
> + int err, i;
> +
> + err = of_property_read_string(np, "mac-mode", &pm);
> + if (err < 0)
> + return err;
> +
> + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) {
> + if (!strcasecmp(pm, phy_modes(i)))
> + return i;
> + }
> +
> + return -ENODEV;
> +}
> +
> static phy_interface_t socfpga_get_plat_phymode(struct socfpga_dwmac *dwmac)
> {
> - return dwmac->plat_dat->phy_interface;
> + return dwmac->mac_interface;
> }
Taking a look at the logic in socfpga_gen{5|10}_set_phy_mode(), we have :
phy_interface_t phymode = socfpga_get_plat_phymode(dwmac);
u32 val;
socfpga_set_phy_mode_common(phymode, &val)
if (dwmac->splitter_base)
val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
[...]
if (phymode == PHY_INTERFACE_MODE_SGMII)
socfpga_sgmii_config(dwmac, true);
With this new patch, we now have 2 different ways of handling this
converter block (splitter presence, and mac-mode presence in DT)
Can you unify this a bit ?
One thing could be adding a helper to get the macmode such as
socfpga_get_plat_macmode()
I think we should move the splitter handling before calling
socfpga_get_plat_macmode() :
if (dwmac->splitter_base)
dwmav->mac_interface = PHY_INTERFACE_MODE_GMII
We'd get the intf_sel value based on the macmode, and also calls to
helpers such as socfpga_sgmii_config(phymode, xxx) need the actual
phymode as a parameter, not the macmode.
Thanks :)
Maxime
^ permalink raw reply
* Re: [PATCH] net: airoha: fix MIB stats collection to be lossless
From: Lorenzo Bianconi @ 2026-07-01 6:51 UTC (permalink / raw)
To: Aniket Negi
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, linux-arm-kernel, linux-mediatek, netdev,
linux-kernel, aniket.negi
In-Reply-To: <20260701063823.239783-1-aniket.negi03@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 5633 bytes --]
> Hi Lorenzo,
>
> Thank you for the detailed review and suggestions!
>
> > > + /* TX - 64-bit H+L registers: hw accumulates the total, read directly. =
> > */
> > > val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
> > > - dev->stats.tx_ok_pkts += ((u64)val << 32);
> > > - val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
> > > - dev->stats.tx_ok_pkts += val;
> > > + dev->stats.tx_ok_pkts = (u64)val << 32;
> >
> > I guess it is more readable to store REG_FE_GDM_TX_OK_PKT_CNT_L() read in v=
> > al
> > here. Something like:
> >
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
> > dev->stats.tx_ok_pkts += val;
> >
> > This apply even to occurrence below
> Agreed. I'll store CNT_L read in val first to improve readability.
>
> > > + dev->stats.tx_ok_pkts += airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L=
> > (port->id));
> > > =20
> > > val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
> > > - dev->stats.tx_ok_bytes += ((u64)val << 32);
> > > - val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
> > > - dev->stats.tx_ok_bytes += val;
> > > + dev->stats.tx_ok_bytes = (u64)val << 32;
> > > + dev->stats.tx_ok_bytes += airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT=
> > _L(port->id));
> > > =20
> > > + /* TX - 32-bit registers: accumulate delta to handle wrap-around. */
> > > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
> > > - dev->stats.tx_drops += val;
> > > + dev->stats.tx_drops += (u32)(val - dev->stats.hw_prev_stats.tx_drops);
> > > + dev->stats.hw_prev_stats.tx_drops = val;
> > > =20
> > > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
> > > - dev->stats.tx_broadcast += val;
> > > + dev->stats.tx_broadcast += (u32)(val - dev->stats.hw_prev_stats.tx_br=
> > oadcast);
> > > + dev->stats.hw_prev_stats.tx_broadcast = val;
> > > =20
> > > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
> > > - dev->stats.tx_multicast += val;
> > > + dev->stats.tx_multicast += (u32)(val - dev->stats.hw_prev_stats.tx_mu=
> > lticast);
> > > + dev->stats.hw_prev_stats.tx_multicast = val;
> > > =20
> > > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
> > > - dev->stats.tx_len[i] += val;
> > > + dev->stats.tx_len[i] += (u32)(val - dev->stats.hw_prev_stats.tx_len[i=
> > ]);
> > > + dev->stats.hw_prev_stats.tx_len[i] = val;
> > > =20
> > > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
> > > - dev->stats.tx_len[i] += ((u64)val << 32);
> > > - val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
> > > - dev->stats.tx_len[i++] += val;
> > > + dev->stats.tx_len[i] += (u64)val << 32;
> >
> > Since now we do not reset MIB counters, this is wrong, you can't use "+="
>
> You are absolutely right, since MIB counters are no longer cleared, using "+=" for E64 counter would cause double counting each iteration. This was missed in the patch, specifically for the case where runt count(32 bit) and E64 counter (64 bit) need to be combined in the same counter.
>
> I'll fix this by using separate accumulator fields to "tx_runt_accum/rx_runt_accum" to track the runt deltas, then compute tx_len[i] as tx_len[i]= tx_runt_accum + E64_CNT (H+L).
>
> > > val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
> > > - dev->stats.rx_len[i] += val;
> > > + dev->stats.rx_len[i] += (u32)(val - dev->stats.hw_prev_stats.rx_len[i=
> > ]);
> > > + dev->stats.hw_prev_stats.rx_len[i] = val;
> > > =20
> > > val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
> > > - dev->stats.rx_len[i] += ((u64)val << 32);
> > > - val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
> > > - dev->stats.rx_len[i++] += val;
> > > + dev->stats.rx_len[i] += (u64)val << 32;
> >
> > same here.
>
> Acked. The same approach above will be applied to rx_len[i].
>
> > > +
> > > + struct {
> > > + /* Previous HW register values for 32-bit counter delta tracking.
> > > + * Storing the last seen value and accumulating (u32)(curr - prev)
> > > + * in 64-bit software counter & handles wrap-around transparently
> > > + * via unsigned arithmetic. These fields are never reported to
> > > + * userspace.
> > > + */
> >
> > can you please align the comment here?
>
> Will fix the comment alignment.
>
> >
> > > + u32 tx_drops;
> > > + u32 tx_broadcast;
> > > + u32 tx_multicast;
> > > + u32 tx_len[7];
> > > + u32 rx_drops;
> > > + u32 rx_broadcast;
> > > + u32 rx_multicast;
> > > + u32 rx_errors;
> > > + u32 rx_crc_error;
> > > + u32 rx_over_errors;
> > > + u32 rx_fragment;
> > > + u32 rx_jabber;
> > > + u32 rx_len[7];
> > > + } hw_prev_stats;
> >
> > Maybe something like "prev_val32" ?
> >
> > Will update the name of struct to hold prev counter from hw_pre_stats to prev_val32.
>
> Good suggestion. However, since the struct hw_prev_stats now contains both u32 (previous register value) and u64 (runt accumulators) fields. I'll rename it to "prev_mib_state" to better reflect its dual purpose of storing previous register values for delta calculation and accumulators for combined counters.
Maybe better mib_prev?
Since now we do not reset the MIB counters in airoha_update_hw_stats(), we can
get rid of the for loop there and just call airoha_dev_get_hw_stats() with the
provided dev pointer. Even better, just rename airoha_dev_get_hw_stats() in
airoha_update_hw_stats() and move the spinlock there. What do you think?
Regards,
Lorenzo
>
> Regards,
> Aniket Negi
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v4 1/5] dt-bindings: arm: coresight-tnoc: Add standalone qcom,coresight-agtnoc compatible
From: Krzysztof Kozlowski @ 2026-07-01 6:57 UTC (permalink / raw)
To: Jie Gan
Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Tingwei Zhang, Jingyi Wang, Abel Vesa,
Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Yuanfang Zhang, Abel Vesa, Alexander Shishkin, Konrad Dybcio,
linux-arm-msm, devicetree, linux-kernel, coresight,
linux-arm-kernel
In-Reply-To: <20260701-fix-tracenoc-probe-issue-v4-1-aefab449a470@oss.qualcomm.com>
On Wed, Jul 01, 2026 at 09:53:41AM +0800, Jie Gan wrote:
> The TNOC compatible previously only allowed the two-string AMBA form
> "qcom,coresight-tnoc", "arm,primecell", which forces the device onto the
> AMBA bus.
>
> Convert the compatible to a oneOf and add a standalone
> "qcom,coresight-agtnoc" compatible alongside the existing AMBA form. The
> standalone string carries no "arm,primecell" entry, so the device is
> created on the platform bus instead of the AMBA bus.
That's a Linux driver specific reason, so not a valid one.
Describe the hardware instead.
Also, you do not get other compatible for the same device.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v4 2/5] coresight: tnoc: add AG tnoc standalone compatible to the platform driver
From: Krzysztof Kozlowski @ 2026-07-01 6:58 UTC (permalink / raw)
To: Jie Gan
Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Tingwei Zhang, Jingyi Wang, Abel Vesa,
Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Yuanfang Zhang, Abel Vesa, Alexander Shishkin, Konrad Dybcio,
linux-arm-msm, devicetree, linux-kernel, coresight,
linux-arm-kernel
In-Reply-To: <20260701-fix-tracenoc-probe-issue-v4-2-aefab449a470@oss.qualcomm.com>
On Wed, Jul 01, 2026 at 09:53:42AM +0800, Jie Gan wrote:
> The Aggregator TNOC can be described either as an AMBA device using the
> "qcom,coresight-tnoc", "arm,primecell" compatible or as a standalone
> platform device using the new "qcom,coresight-agtnoc" compatible. The
> latter avoids the AMBA bus and the associated peripheral-ID probing.
So here it is: you added a fake hardware description so the driver can
avoid some action.
No, instead fix the drivers. Hardware did not change, thus you should
not change the bindings.
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH v3 0/3] Add AST2700 USB3.2 PHY driver
From: Ryan Chen @ 2026-07-01 6:58 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Joel Stanley, Andrew Jeffery, Philipp Zabel
Cc: linux-phy, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel, Ryan Chen, Krzysztof Kozlowski
Add AST2700 USB3.2 PHY support.
- Supports Super Speed Plus Gen2x1 (10 Gbps), Super Speed (5 Gbps),
High Speed (480 Mbps), Full Speed (12Mbps), and Low Speed (1.5 Mbps).
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
---
Changes in v3:
- Wire drivers/phy/aspeed/ into drivers/phy/Kconfig and
drivers/phy/Makefile so the driver is actually built.
- Fix the Makefile config symbol to CONFIG_PHY_ASPEED_USB3 to match
the Kconfig symbol.
- Expand the Kconfig help text.
- Link to v2: https://lore.kernel.org/r/20260116-upstream_usb3phy-v2-0-0b0c9f3eb6f4@aspeedtech.com
Changes in v2:
- aspeed,ast2700-usb3-phy.yaml
- Drop clocks, resets descripton.
- Kconfig
- add COMPILE_TEST, remove default n
- Link to v1: https://lore.kernel.org/r/20260114-upstream_usb3phy-v1-0-2e59590be2d7@aspeedtech.com
---
Ryan Chen (3):
dt-bindings: phy: aspeed: Document AST2700 USB3.2 PHY
phy: aspeed: Add AST2700 USB3.2 PHY driver
MAINTAINERS: Add ASPEED USB3 PHY driver
.../bindings/phy/aspeed,ast2700-usb3-phy.yaml | 48 +++++
MAINTAINERS | 8 +
drivers/phy/Kconfig | 1 +
drivers/phy/Makefile | 1 +
drivers/phy/aspeed/Kconfig | 15 ++
drivers/phy/aspeed/Makefile | 2 +
drivers/phy/aspeed/phy-aspeed-usb3.c | 236 +++++++++++++++++++++
7 files changed, 311 insertions(+)
---
base-commit: 948efecf22e49aa4bf55bb73ec79a0ddcfd38571
change-id: 20260112-upstream_usb3phy-7116f8dfe779
Best regards,
--
Ryan Chen <ryan_chen@aspeedtech.com>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox