Add "select" node for each step to determine if another step is taken,
trigger(s) are generated, counters/timers incremented/decremented, etc.
Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
---
.../ABI/testing/sysfs-bus-amba-devices-tgu | 7 +++
drivers/hwtracing/qcom/tgu.c | 57 ++++++++++++++++++-
drivers/hwtracing/qcom/tgu.h | 27 +++++++++
3 files changed, 89 insertions(+), 2 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
index 4efa36a11d8e..fffe65d3c0db 100644
--- a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
+++ b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
@@ -21,3 +21,10 @@ KernelVersion 6.19
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: January 2026
+KernelVersion 6.19
+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 39301d35ee9d..e1a1f0f423ba 100644
--- a/drivers/hwtracing/qcom/tgu.c
+++ b/drivers/hwtracing/qcom/tgu.c
@@ -33,6 +33,10 @@ static int calculate_array_location(struct tgu_drvdata *drvdata,
ret = step_index * (drvdata->max_condition_decode) +
reg_index;
break;
+ case TGU_CONDITION_SELECT:
+ ret = step_index * (drvdata->max_condition_select) +
+ reg_index;
+ break;
default:
break;
}
@@ -74,6 +78,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;
}
@@ -115,6 +122,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;
@@ -148,6 +159,15 @@ static umode_t tgu_node_visible(struct kobject *kobject,
drvdata->max_condition_decode) ?
attr->mode : 0;
break;
+ case TGU_CONDITION_SELECT:
+ /* 'default' register is at the end of 'select' region */
+ if (tgu_attr->reg_num ==
+ drvdata->max_condition_select - 1)
+ attr->name = "default";
+ ret = (tgu_attr->reg_num <
+ drvdata->max_condition_select) ?
+ attr->mode : 0;
+ break;
default:
break;
}
@@ -186,6 +206,19 @@ static ssize_t tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
drvdata->base + CONDITION_DECODE_STEP(i, j));
}
}
+
+ for (i = 0; i < drvdata->max_step; i++) {
+ for (j = 0; j < drvdata->max_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:
@@ -225,6 +258,8 @@ static void tgu_set_conditions(struct tgu_drvdata *drvdata)
devid = readl(drvdata->base + TGU_DEVID);
drvdata->max_condition_decode = TGU_DEVID_CONDITIONS(devid);
+ /* select region has an additional 'default' register */
+ drvdata->max_condition_select = TGU_DEVID_CONDITIONS(devid) + 1;
}
static int tgu_enable(struct device *dev)
@@ -356,6 +391,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,
};
@@ -363,8 +406,8 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
struct tgu_drvdata *drvdata;
- size_t priority_size, condition_size;
- unsigned int *priority, *condition;
+ size_t priority_size, condition_size, select_size;
+ unsigned int *priority, *condition, *select;
int ret;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
@@ -417,6 +460,16 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->value_table->condition_decode = condition;
+ select_size = drvdata->max_condition_select * drvdata->max_step *
+ sizeof(*(drvdata->value_table->condition_select));
+
+ select = devm_kzalloc(dev, select_size, GFP_KERNEL);
+
+ if (!select)
+ return -ENOMEM;
+
+ drvdata->value_table->condition_select = select;
+
drvdata->enable = false;
pm_runtime_put(&adev->dev);
diff --git a/drivers/hwtracing/qcom/tgu.h b/drivers/hwtracing/qcom/tgu.h
index 0d058663aca5..8c92e88d7e2c 100644
--- a/drivers/hwtracing/qcom/tgu.h
+++ b/drivers/hwtracing/qcom/tgu.h
@@ -50,6 +50,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
@@ -61,6 +62,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,9 @@
#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), \
STEP_PRIORITY(step_index, 1, priority), \
@@ -106,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),\
@@ -120,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 */
@@ -141,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)
@@ -171,6 +196,7 @@ static inline void TGU_UNLOCK(void __iomem *addr)
* @max_reg: Maximum number of registers
* @max_step: Maximum step size
* @max_condition_decode: Maximum number of condition_decode
+ * @max_condition_select: Maximum number of condition_select
*
* This structure defines the data associated with a TGU device,
* including its base address, device pointers, clock, spinlock for
@@ -186,6 +212,7 @@ struct tgu_drvdata {
int max_reg;
int max_step;
int max_condition_decode;
+ int max_condition_select;
};
#endif
--
2.34.1
On 1/9/26 3:11 AM, Songwei Chai wrote:
> Add "select" node for each step to determine if another step is taken,
> trigger(s) are generated, counters/timers incremented/decremented, etc.
>
> Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
> ---
[...]
> + case TGU_CONDITION_SELECT:
> + /* 'default' register is at the end of 'select' region */
> + if (tgu_attr->reg_num ==
> + drvdata->max_condition_select - 1)
> + attr->name = "default";
> + ret = (tgu_attr->reg_num <
> + drvdata->max_condition_select) ?
> + attr->mode : 0;
similarly to my previous comments
[...]
> + for (i = 0; i < drvdata->max_step; i++) {
> + for (j = 0; j < drvdata->max_condition_select; j++) {
> + index = check_array_location(drvdata, i,
> + TGU_CONDITION_SELECT, j);
> +
> + if (index == -EINVAL)
stray \n
> + 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:
> @@ -225,6 +258,8 @@ static void tgu_set_conditions(struct tgu_drvdata *drvdata)
>
> devid = readl(drvdata->base + TGU_DEVID);
> drvdata->max_condition_decode = TGU_DEVID_CONDITIONS(devid);
> + /* select region has an additional 'default' register */
> + drvdata->max_condition_select = TGU_DEVID_CONDITIONS(devid) + 1;
> }
>
> static int tgu_enable(struct device *dev)
> @@ -356,6 +391,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,
> };
>
> @@ -363,8 +406,8 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
> {
> struct device *dev = &adev->dev;
> struct tgu_drvdata *drvdata;
> - size_t priority_size, condition_size;
> - unsigned int *priority, *condition;
> + size_t priority_size, condition_size, select_size;
> + unsigned int *priority, *condition, *select;
> int ret;
>
> drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> @@ -417,6 +460,16 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
>
> drvdata->value_table->condition_decode = condition;
>
> + select_size = drvdata->max_condition_select * drvdata->max_step *
> + sizeof(*(drvdata->value_table->condition_select));
> +
> + select = devm_kzalloc(dev, select_size, GFP_KERNEL);
> +
> + if (!select)
stray \n
> + return -ENOMEM;
> +
> + drvdata->value_table->condition_select = select;
I don't see a need for an intemediate variable here
[...]
> * @max_condition_decode: Maximum number of condition_decode
> + * @max_condition_select: Maximum number of condition_select
Maximum value, perhaps? You haven't explained the feature very well
so I'm not sure what this is supposed to reflect
Konrad
On 1/13/2026 7:15 PM, Konrad Dybcio wrote:
> On 1/9/26 3:11 AM, Songwei Chai wrote:
>> Add "select" node for each step to determine if another step is taken,
>> trigger(s) are generated, counters/timers incremented/decremented, etc.
>>
>> Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
>> ---
>
> [...]
>
>> + case TGU_CONDITION_SELECT:
>> + /* 'default' register is at the end of 'select' region */
>> + if (tgu_attr->reg_num ==
>> + drvdata->max_condition_select - 1)
>> + attr->name = "default";
>> + ret = (tgu_attr->reg_num <
>> + drvdata->max_condition_select) ?
>> + attr->mode : 0;
>
> similarly to my previous comments
>
> [...]
>
>> + for (i = 0; i < drvdata->max_step; i++) {
>> + for (j = 0; j < drvdata->max_condition_select; j++) {
>> + index = check_array_location(drvdata, i,
>> + TGU_CONDITION_SELECT, j);
>> +
>> + if (index == -EINVAL)
>
> stray \n
>> + 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:
>> @@ -225,6 +258,8 @@ static void tgu_set_conditions(struct tgu_drvdata *drvdata)
>>
>> devid = readl(drvdata->base + TGU_DEVID);
>> drvdata->max_condition_decode = TGU_DEVID_CONDITIONS(devid);
>> + /* select region has an additional 'default' register */
>> + drvdata->max_condition_select = TGU_DEVID_CONDITIONS(devid) + 1;
>> }
>>
>> static int tgu_enable(struct device *dev)
>> @@ -356,6 +391,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,
>> };
>>
>> @@ -363,8 +406,8 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
>> {
>> struct device *dev = &adev->dev;
>> struct tgu_drvdata *drvdata;
>> - size_t priority_size, condition_size;
>> - unsigned int *priority, *condition;
>> + size_t priority_size, condition_size, select_size;
>> + unsigned int *priority, *condition, *select;
>> int ret;
>>
>> drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
>> @@ -417,6 +460,16 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
>>
>> drvdata->value_table->condition_decode = condition;
>>
>> + select_size = drvdata->max_condition_select * drvdata->max_step *
>> + sizeof(*(drvdata->value_table->condition_select));
>> +
>> + select = devm_kzalloc(dev, select_size, GFP_KERNEL);
>> +
>> + if (!select)
>
> stray \n
Will improve this based on the comments above.
>
>> + return -ENOMEM;
>> +
>> + drvdata->value_table->condition_select = select;
>
> I don't see a need for an intemediate variable here
This was done intentionally, following the earlier suggestion in v9 to
introduce named intermediate variables for better readability when
dealing with allocations.
I’m happy to inline the allocation if you prefer the simpler form here.
>
> [...]
>
>> * @max_condition_decode: Maximum number of condition_decode
>> + * @max_condition_select: Maximum number of condition_select
>
> Maximum value, perhaps? You haven't explained the feature very well
> so I'm not sure what this is supposed to reflect
Will add "Maximum value" description in cover letter.
>
> Konrad
On 1/27/26 3:43 AM, Songwei Chai wrote: > > > On 1/13/2026 7:15 PM, Konrad Dybcio wrote: >> On 1/9/26 3:11 AM, Songwei Chai wrote: >>> Add "select" node for each step to determine if another step is taken, >>> trigger(s) are generated, counters/timers incremented/decremented, etc. >>> >>> Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com> >>> --- [...] >>> + select = devm_kzalloc(dev, select_size, GFP_KERNEL); >>> + >>> + if (!select) >> >> stray \n > Will improve this based on the comments above. >> >>> + return -ENOMEM; >>> + >>> + drvdata->value_table->condition_select = select; >> >> I don't see a need for an intemediate variable here > > This was done intentionally, following the earlier suggestion in v9 to > introduce named intermediate variables for better readability when dealing with allocations. > > I’m happy to inline the allocation if you prefer the simpler form here. I don't mind that much Konrad
© 2016 - 2026 Red Hat, Inc.