[PATCH v2 6/7] coresight-tgu: add timer/counter functionality for TGU

From: Songwei Chai
Date: Thu Oct 10 2024 - 04:24:47 EST


Add counter and timer node for each step which could be
programed if they are to be utilized in trigger event/sequence.

Signed-off-by: Songwei Chai <quic_songchai@xxxxxxxxxxx>
---
.../testing/sysfs-bus-coresight-devices-tgu | 14 +++
drivers/hwtracing/coresight/coresight-tgu.c | 95 ++++++++++++++++++-
drivers/hwtracing/coresight/coresight-tgu.h | 47 ++++++++-
3 files changed, 154 insertions(+), 2 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tgu b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tgu
index 8dce2b46b48a..e404e0d6f8f0 100644
--- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tgu
+++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tgu
@@ -28,3 +28,17 @@ KernelVersion 6.10
Contact: Jinlong Mao (QUIC) <quic_jinlmao@xxxxxxxxxxx>, Sam Chai (QUIC) <quic_songchai@xxxxxxxxxxx>
Description:
(RW) Set/Get the next action with specific step for TGU.
+
+What: /sys/bus/coresight/devices/<tgu-name>/step[0:7]_timer/reg[0:1]
+Date: August 2024
+KernelVersion 6.10
+Contact: Jinlong Mao (QUIC) <quic_jinlmao@xxxxxxxxxxx>, Sam Chai (QUIC) <quic_songchai@xxxxxxxxxxx>
+Description:
+ (RW) Set/Get the timer value with specific step for TGU.
+
+What: /sys/bus/coresight/devices/<tgu-name>/step[0:7]_counter/reg[0:1]
+Date: August 2024
+KernelVersion 6.10
+Contact: Jinlong Mao (QUIC) <quic_jinlmao@xxxxxxxxxxx>, Sam Chai (QUIC) <quic_songchai@xxxxxxxxxxx>
+Description:
+ (RW) Set/Get the counter value with specific step for TGU.
diff --git a/drivers/hwtracing/coresight/coresight-tgu.c b/drivers/hwtracing/coresight/coresight-tgu.c
index 860fab854b95..05456e23b852 100644
--- a/drivers/hwtracing/coresight/coresight-tgu.c
+++ b/drivers/hwtracing/coresight/coresight-tgu.c
@@ -37,6 +37,10 @@ static int calculate_array_location(struct tgu_drvdata *drvdata, int step_index,
case TGU_CONDITION_SELECT:
ret = step_index * (drvdata->max_condition_select) + reg_index;
break;
+ case TGU_COUNTER:
+ case TGU_TIMER:
+ ret = step_index * (drvdata->max_timer_counter) + reg_index;
+ break;
default:
break;
}
@@ -70,7 +74,16 @@ static ssize_t tgu_dataset_show(struct device *dev,
drvdata->value_table->condition_select[calculate_array_location(
drvdata, tgu_attr->step_index, tgu_attr->operation_index,
tgu_attr->reg_num)]);
-
+ case TGU_TIMER:
+ return sysfs_emit(buf, "0x%x\n",
+ drvdata->value_table->timer[calculate_array_location(
+ drvdata, tgu_attr->step_index, tgu_attr->operation_index,
+ tgu_attr->reg_num)]);
+ case TGU_COUNTER:
+ return sysfs_emit(buf, "0x%x\n",
+ drvdata->value_table->counter[calculate_array_location(
+ drvdata, tgu_attr->step_index, tgu_attr->operation_index,
+ tgu_attr->reg_num)]);
}
return -EINVAL;

@@ -113,6 +126,18 @@ static ssize_t tgu_dataset_store(struct device *dev,
tgu_attr->reg_num)] = val;
ret = size;
break;
+ case TGU_TIMER:
+ tgu_drvdata->value_table->timer[calculate_array_location(
+ tgu_drvdata, tgu_attr->step_index, tgu_attr->operation_index,
+ tgu_attr->reg_num)] = val;
+ ret = size;
+ break;
+ case TGU_COUNTER:
+ tgu_drvdata->value_table->counter[calculate_array_location(
+ tgu_drvdata, tgu_attr->step_index, tgu_attr->operation_index,
+ tgu_attr->reg_num)] = val;
+ ret = size;
+ break;
default:
break;
}
@@ -153,6 +178,15 @@ static umode_t tgu_node_visible(struct kobject *kobject, struct attribute *attr,
drvdata->max_condition_select) ?
attr->mode : 0;
break;
+ case TGU_COUNTER:
+ case TGU_TIMER:
+ if (drvdata->max_timer_counter == 0)
+ ret = SYSFS_GROUP_INVISIBLE;
+ else
+ ret = (tgu_attr->reg_num <
+ drvdata->max_timer_counter) ?
+ attr->mode : 0;
+ break;
default:
break;
}
@@ -200,6 +234,26 @@ static void tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
}
}

+ for (i = 0; i < drvdata->max_step; i++) {
+ for (j = 0; j < drvdata->max_timer_counter; j++) {
+ tgu_writel(drvdata,
+ drvdata->value_table->timer
+ [calculate_array_location(drvdata, i,
+ TGU_TIMER, j)],
+ TIMER0_COMPARE_STEP(i, j));
+ }
+ }
+
+ for (i = 0; i < drvdata->max_step; i++) {
+ for (j = 0; j < drvdata->max_timer_counter; j++) {
+ tgu_writel(drvdata,
+ drvdata->value_table->counter
+ [calculate_array_location(drvdata, i,
+ TGU_COUNTER, j)],
+ COUNTER0_COMPARE_STEP(i, j));
+ }
+ }
+
/* Enable TGU to program the triggers */
tgu_writel(drvdata, 1, TGU_CONTROL);
CS_LOCK(drvdata->base);
@@ -358,6 +412,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,
};

@@ -407,6 +477,11 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
if (ret)
return -EINVAL;

+ ret = of_property_read_u32(adev->dev.of_node, "tgu-timer-counters",
+ &drvdata->max_timer_counter);
+ if (ret)
+ return -EINVAL;
+
drvdata->max_condition_decode = drvdata->max_condition;
/* select region has an additional 'default' register */
drvdata->max_condition_select = drvdata->max_condition + 1;
@@ -443,6 +518,24 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
if (!drvdata->value_table->condition_select)
return -ENOMEM;

+ drvdata->value_table->timer = devm_kzalloc(
+ dev,
+ drvdata->max_step * drvdata->max_timer_counter *
+ sizeof(*(drvdata->value_table->timer)),
+ GFP_KERNEL);
+
+ if (!drvdata->value_table->timer)
+ return -ENOMEM;
+
+ drvdata->value_table->counter = devm_kzalloc(
+ dev,
+ drvdata->max_step * drvdata->max_timer_counter *
+ sizeof(*(drvdata->value_table->counter)),
+ GFP_KERNEL);
+
+ if (!drvdata->value_table->counter)
+ return -ENOMEM;
+
drvdata->enable = false;
desc.type = CORESIGHT_DEV_TYPE_HELPER;
desc.pdata = adev->dev.platform_data;
diff --git a/drivers/hwtracing/coresight/coresight-tgu.h b/drivers/hwtracing/coresight/coresight-tgu.h
index 0d3b966dafc7..9fa23033b4af 100644
--- a/drivers/hwtracing/coresight/coresight-tgu.h
+++ b/drivers/hwtracing/coresight/coresight-tgu.h
@@ -52,6 +52,12 @@
#define CONDITION_SELECT_STEP(step, select) \
(0x0060 + 0x4 * select + 0x1D8 * step)

+#define TIMER0_COMPARE_STEP(step, timer) \
+ (0x0040 + 0x4 * timer + 0x1D8 * step)
+
+#define COUNTER0_COMPARE_STEP(step, counter) \
+ (0x0048 + 0x4 * counter + 0x1D8 * step)
+
#define tgu_dataset_ro(name, step_index, type, reg_num) \
(&((struct tgu_attribute[]){ { \
__ATTR(name, 0444, tgu_dataset_show, NULL), \
@@ -78,6 +84,12 @@
#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), \
STEP_PRIORITY(step_index, 1, priority), \
@@ -117,6 +129,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),\
@@ -138,13 +162,29 @@
.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,
TGU_PRIORITY2,
TGU_PRIORITY3,
TGU_CONDITION_DECODE,
- TGU_CONDITION_SELECT
+ TGU_CONDITION_SELECT,
+ TGU_TIMER,
+ TGU_COUNTER
};

/* Maximum priority that TGU supports */
@@ -161,6 +201,8 @@ struct value_table {
unsigned int *priority;
unsigned int *condition_decode;
unsigned int *condition_select;
+ unsigned int *timer;
+ unsigned int *counter;
};

/**
@@ -176,6 +218,8 @@ struct value_table {
* @max_condition: Maximum number of condition
* @max_condition_decode: Maximum number of condition_decode
* @max_condition_select: Maximum number of condition_select
+ * @max_timer_counter: Maximum number of timers and counters
+ *
* 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.
@@ -192,6 +236,7 @@ struct tgu_drvdata {
int max_condition;
int max_condition_decode;
int max_condition_select;
+ int max_timer_counter;
};

#endif