* Re: [PATCH 3/4] perf arm_spe: Decode Arm N1 IMPDEF events
From: Leo Yan @ 2026-04-02 9:32 UTC (permalink / raw)
To: James Clark
Cc: John Garry, Will Deacon, Mike Leach, Leo Yan, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
Al Grant, linux-arm-kernel, linux-perf-users, linux-kernel
In-Reply-To: <20260401-james-spe-impdef-decode-v1-3-ad0d372c220c@linaro.org>
On Wed, Apr 01, 2026 at 03:25:51PM +0100, James Clark wrote:
[...]
> static int arm_spe_pkt_desc_event(const struct arm_spe_pkt *packet,
> - char *buf, size_t buf_len)
> + char *buf, size_t buf_len, u64 midr)
Just wandering if we can add a "midr" field into struct arm_spe_pkt ?
Then we don't need to change many functions for populating midr.
The rest is good for me.
Thanks,
Leo
^ permalink raw reply
* [PATCH v13 6/7] qcom-tgu: Add timer/counter functionality for TGU
From: Songwei Chai @ 2026-04-02 9:28 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: <20260402092838.341295-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 | 56 +++++++++
3 files changed, 184 insertions(+), 2 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
index 786cb852bbe5..7a3573e03e27 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.1
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: April 2026
+KernelVersion: 7.1
+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: April 2026
+KernelVersion: 7.1
+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 4112e6a691d6..4a529520b428 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 ac46a2875209..5dfef0afbad6 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,19 @@
.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,
@@ -151,6 +199,8 @@ enum operation_index {
TGU_PRIORITY3,
TGU_CONDITION_DECODE,
TGU_CONDITION_SELECT,
+ TGU_TIMER,
+ TGU_COUNTER
};
/* Maximum priority that TGU supports */
@@ -167,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)
@@ -198,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
@@ -214,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 v13 7/7] qcom-tgu: Add reset node to initialize
From: Songwei Chai @ 2026-04-02 9:28 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
In-Reply-To: <20260402092838.341295-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>
---
.../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 7a3573e03e27..a6b6019c8ef1 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.1
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: April 2026
+KernelVersion: 7.1
+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 4a529520b428..752fcd2963e1 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 v13 4/7] qcom-tgu: Add TGU decode support
From: Songwei Chai @ 2026-04-02 9:28 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: <20260402092838.341295-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 223873789ca6..4ef0d696d3d0 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.1
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: April 2026
+KernelVersion: 7.1
+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..5b37eb10f863 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 arrary 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 df570c89ffd7..987ea07bd618 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 v13 5/7] qcom-tgu: Add support to configure next action
From: Songwei Chai @ 2026-04-02 9:28 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: <20260402092838.341295-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 | 27 ++++++++++
3 files changed, 85 insertions(+), 2 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
index 4ef0d696d3d0..786cb852bbe5 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.1
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: April 2026
+KernelVersion: 7.1
+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 5b37eb10f863..4112e6a691d6 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 987ea07bd618..ac46a2875209 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,21 @@
.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 +166,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 +197,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 +213,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 v13 3/7] qcom-tgu: Add signal priority support
From: Songwei Chai @ 2026-04-02 9:28 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: <20260402092838.341295-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 f877a00fcaa5..223873789ca6 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: April 2026
+KernelVersion: 7.1
+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..df570c89ffd7 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$
+ * | registe | |---> |prioroty[3] |$
+ * | | | |-------------|base+n*0x1D8+0x194$
+ * | | | |prioroty[2] |$
+ * |-------------------------| | |-------------|base+n*0x1D8+0x134$
+ * | | | |prioroty[1] |$
+ * | step[7] | | |-------------|base+n*0x1D8+0xD4$
+ * |-------------------------|->base+0x40+7*0x1D8 | |prioroty[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 v13 2/7] qcom-tgu: Add TGU driver
From: Songwei Chai @ 2026-04-02 9:28 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
In-Reply-To: <20260402092838.341295-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>
---
.../ABI/testing/sysfs-bus-amba-devices-tgu | 9 +
drivers/Makefile | 1 +
drivers/hwtracing/Kconfig | 2 +
drivers/hwtracing/qcom/Kconfig | 18 ++
drivers/hwtracing/qcom/Makefile | 3 +
drivers/hwtracing/qcom/tgu.c | 193 ++++++++++++++++++
drivers/hwtracing/qcom/tgu.h | 51 +++++
7 files changed, 277 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..f877a00fcaa5
--- /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: April 2026
+KernelVersion: 7.1
+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/drivers/Makefile b/drivers/Makefile
index 53fbd2e0acdd..82b712a12a26 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -177,6 +177,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..d6f6d4b0f28e
--- /dev/null
+++ b/drivers/hwtracing/qcom/Kconfig
@@ -0,0 +1,18 @@
+# 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"
+ 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 v13 0/7] Provide support for Trigger Generation Unit
From: Songwei Chai @ 2026-04-02 9:28 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 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 ++
drivers/Makefile | 1 +
drivers/hwtracing/Kconfig | 2 +
drivers/hwtracing/qcom/Kconfig | 18 +
drivers/hwtracing/qcom/Makefile | 3 +
drivers/hwtracing/qcom/tgu.c | 704 ++++++++++++++++++
drivers/hwtracing/qcom/tgu.h | 275 +++++++
8 files changed, 1125 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 v13 1/7] dt-bindings: arm: Add support for Qualcomm TGU trace
From: Songwei Chai @ 2026-04-02 9:28 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: <20260402092838.341295-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 5/8] firmware: sysfb: Implement screen_info relocation for primary display
From: Thomas Zimmermann @ 2026-04-02 9:09 UTC (permalink / raw)
To: javierm, arnd, ardb, ilias.apalodimas, chenhuacai, kernel,
maarten.lankhorst, mripard, airlied, simona, kys, haiyangz,
wei.liu, decui, longli, deller
Cc: linux-arm-kernel, loongarch, linux-efi, linux-riscv, dri-devel,
linux-hyperv, linux-fbdev, Thomas Zimmermann
In-Reply-To: <20260402092305.208728-1-tzimmermann@suse.de>
Move the relocation tracking for screen_info from the screen_info
helpers to sysfb. The relocation code operates on sysfb_primary_display,
which belongs to sysfb. The remaining screen_info helpers are now free
from global state.
Adapt some symbol names. Now prefer early returns in the helper
sysfb_apply_screen_info_fixup() over nested branching. Also return an
errno code from sysfb_apply_screen_info_fixup() if the relocation
failed. In this case, do not create a device for the framebuffer.
The original code advertised this behavior in a comment but never
implemented it.
Framebuffer aperture relocation can happen during boot if the PCI
graphics device is located behind a PCI bridge. If the bridge's sub-
bus gets relocated, the framebuffer aperture moves accordingly. The
helper for tracking these relocations fixes up the values stored in
sysfb_primary_display so that they refer to the correct address range
again. Generic system-framebuffer drivers would not work otherwise.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/firmware/sysfb.h | 5 ++
drivers/firmware/sysfb_pci.c | 111 +++++++++++++++++++++++++++++++
drivers/firmware/sysfb_primary.c | 9 ++-
drivers/video/screen_info_pci.c | 110 ------------------------------
include/linux/screen_info.h | 3 -
5 files changed, 123 insertions(+), 115 deletions(-)
diff --git a/drivers/firmware/sysfb.h b/drivers/firmware/sysfb.h
index 9f7fe2e03f68..1eaa3b0fa364 100644
--- a/drivers/firmware/sysfb.h
+++ b/drivers/firmware/sysfb.h
@@ -8,8 +8,13 @@
struct pci_dev;
#ifdef CONFIG_PCI
+int sysfb_apply_screen_info_fixups(void);
bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev);
#else
+static inline int sysfb_apply_screen_info_fixups(void)
+{
+ return 0;
+}
static inline bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
{
return false;
diff --git a/drivers/firmware/sysfb_pci.c b/drivers/firmware/sysfb_pci.c
index 8f3adeef4fb1..d972750c6bc6 100644
--- a/drivers/firmware/sysfb_pci.c
+++ b/drivers/firmware/sysfb_pci.c
@@ -1,9 +1,120 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/pci.h>
+#include <linux/printk.h>
+#include <linux/screen_info.h>
+#include <linux/sysfb.h>
#include "sysfb.h"
+static struct pci_dev *sysfb_lfb_pdev;
+static size_t sysfb_lfb_bar;
+static resource_size_t sysfb_lfb_res_start; // original start of resource
+static resource_size_t sysfb_lfb_offset; // framebuffer offset within resource
+
+static bool __sysfb_relocation_is_valid(const struct screen_info *si, struct resource *pr)
+{
+ u64 size = __screen_info_lfb_size(si, screen_info_video_type(si));
+
+ if (sysfb_lfb_offset > resource_size(pr))
+ return false;
+ if (size > resource_size(pr))
+ return false;
+ if (resource_size(pr) - size < sysfb_lfb_offset)
+ return false;
+
+ return true;
+}
+
+int sysfb_apply_screen_info_fixups(void)
+{
+ struct screen_info *si = &sysfb_primary_display.screen;
+ struct resource *pr;
+
+ if (!sysfb_lfb_pdev)
+ return 0; /* primary display is not on a PCI device */
+
+ pr = &sysfb_lfb_pdev->resource[sysfb_lfb_bar];
+
+ if (pr->start == sysfb_lfb_res_start)
+ return 0; /* no relocation took place */
+
+ if (!__sysfb_relocation_is_valid(si, pr))
+ return -ENXIO;
+
+ /*
+ * Only update base if we have an actual relocation to a valid I/O range.
+ */
+ __screen_info_set_lfb_base(si, pr->start + sysfb_lfb_offset);
+ pr_info("Relocating firmware framebuffer to offset %pa[d] within %pr\n",
+ &sysfb_lfb_offset, pr);
+
+ return 0;
+}
+
+static int __screen_info_lfb_pci_bus_region(const struct screen_info *si, unsigned int type,
+ struct pci_bus_region *r)
+{
+ u64 base, size;
+
+ base = __screen_info_lfb_base(si);
+ if (!base)
+ return -EINVAL;
+
+ size = __screen_info_lfb_size(si, type);
+ if (!size)
+ return -EINVAL;
+
+ r->start = base;
+ r->end = base + size - 1;
+
+ return 0;
+}
+
+static void sysfb_fixup_lfb(struct pci_dev *pdev)
+{
+ unsigned int type;
+ struct pci_bus_region bus_region;
+ int ret;
+ struct resource r = {
+ .flags = IORESOURCE_MEM,
+ };
+ const struct resource *pr;
+ const struct screen_info *si = &sysfb_primary_display.screen;
+
+ if (sysfb_lfb_pdev)
+ return; // already found
+
+ type = screen_info_video_type(si);
+ if (!__screen_info_has_lfb(type))
+ return; // only applies to EFI; maybe VESA
+
+ ret = __screen_info_lfb_pci_bus_region(si, type, &bus_region);
+ if (ret < 0)
+ return;
+
+ /*
+ * Translate the PCI bus address to resource. Account for an offset if
+ * the framebuffer is behind a PCI host bridge.
+ */
+ pcibios_bus_to_resource(pdev->bus, &r, &bus_region);
+
+ pr = pci_find_resource(pdev, &r);
+ if (!pr)
+ return;
+
+ /*
+ * We've found a PCI device with the framebuffer resource. Store away
+ * the parameters to track relocation of the framebuffer aperture.
+ */
+ sysfb_lfb_pdev = pdev;
+ sysfb_lfb_bar = pr - pdev->resource;
+ sysfb_lfb_offset = r.start - pr->start;
+ sysfb_lfb_res_start = bus_region.start;
+}
+DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY, 16,
+ sysfb_fixup_lfb);
+
bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
{
/*
diff --git a/drivers/firmware/sysfb_primary.c b/drivers/firmware/sysfb_primary.c
index ab8d7fc468bb..298f87a43a7e 100644
--- a/drivers/firmware/sysfb_primary.c
+++ b/drivers/firmware/sysfb_primary.c
@@ -32,6 +32,7 @@
#include <linux/pci.h>
#include <linux/platform_data/simplefb.h>
#include <linux/platform_device.h>
+#include <linux/printk.h>
#include <linux/screen_info.h>
#include <linux/sysfb.h>
@@ -127,11 +128,15 @@ static __init int sysfb_init(void)
struct simplefb_platform_data mode;
const char *name;
bool compatible;
- int ret = 0;
+ int ret;
- screen_info_apply_fixups();
+ ret = sysfb_apply_screen_info_fixups();
mutex_lock(&disable_lock);
+ if (ret) {
+ pr_warn("Invalid relocation, disabling system framebuffer\n");
+ disabled = true; /* screen_info relocation failed */
+ }
if (disabled)
goto unlock_mutex;
diff --git a/drivers/video/screen_info_pci.c b/drivers/video/screen_info_pci.c
index 8f34d8a74f09..d8985a54ce71 100644
--- a/drivers/video/screen_info_pci.c
+++ b/drivers/video/screen_info_pci.c
@@ -1,117 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/pci.h>
-#include <linux/printk.h>
#include <linux/screen_info.h>
-#include <linux/string.h>
-#include <linux/sysfb.h>
-
-static struct pci_dev *screen_info_lfb_pdev;
-static size_t screen_info_lfb_bar;
-static resource_size_t screen_info_lfb_res_start; // original start of resource
-static resource_size_t screen_info_lfb_offset; // framebuffer offset within resource
-
-static bool __screen_info_relocation_is_valid(const struct screen_info *si, struct resource *pr)
-{
- u64 size = __screen_info_lfb_size(si, screen_info_video_type(si));
-
- if (screen_info_lfb_offset > resource_size(pr))
- return false;
- if (size > resource_size(pr))
- return false;
- if (resource_size(pr) - size < screen_info_lfb_offset)
- return false;
-
- return true;
-}
-
-void screen_info_apply_fixups(void)
-{
- struct screen_info *si = &sysfb_primary_display.screen;
-
- if (screen_info_lfb_pdev) {
- struct resource *pr = &screen_info_lfb_pdev->resource[screen_info_lfb_bar];
-
- if (pr->start != screen_info_lfb_res_start) {
- if (__screen_info_relocation_is_valid(si, pr)) {
- /*
- * Only update base if we have an actual
- * relocation to a valid I/O range.
- */
- __screen_info_set_lfb_base(si, pr->start + screen_info_lfb_offset);
- pr_info("Relocating firmware framebuffer to offset %pa[d] within %pr\n",
- &screen_info_lfb_offset, pr);
- } else {
- pr_warn("Invalid relocating, disabling firmware framebuffer\n");
- }
- }
- }
-}
-
-static int __screen_info_lfb_pci_bus_region(const struct screen_info *si, unsigned int type,
- struct pci_bus_region *r)
-{
- u64 base, size;
-
- base = __screen_info_lfb_base(si);
- if (!base)
- return -EINVAL;
-
- size = __screen_info_lfb_size(si, type);
- if (!size)
- return -EINVAL;
-
- r->start = base;
- r->end = base + size - 1;
-
- return 0;
-}
-
-static void screen_info_fixup_lfb(struct pci_dev *pdev)
-{
- unsigned int type;
- struct pci_bus_region bus_region;
- int ret;
- struct resource r = {
- .flags = IORESOURCE_MEM,
- };
- const struct resource *pr;
- const struct screen_info *si = &sysfb_primary_display.screen;
-
- if (screen_info_lfb_pdev)
- return; // already found
-
- type = screen_info_video_type(si);
- if (!__screen_info_has_lfb(type))
- return; // only applies to EFI; maybe VESA
-
- ret = __screen_info_lfb_pci_bus_region(si, type, &bus_region);
- if (ret < 0)
- return;
-
- /*
- * Translate the PCI bus address to resource. Account
- * for an offset if the framebuffer is behind a PCI host
- * bridge.
- */
- pcibios_bus_to_resource(pdev->bus, &r, &bus_region);
-
- pr = pci_find_resource(pdev, &r);
- if (!pr)
- return;
-
- /*
- * We've found a PCI device with the framebuffer
- * resource. Store away the parameters to track
- * relocation of the framebuffer aperture.
- */
- screen_info_lfb_pdev = pdev;
- screen_info_lfb_bar = pr - pdev->resource;
- screen_info_lfb_offset = r.start - pr->start;
- screen_info_lfb_res_start = bus_region.start;
-}
-DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY, 16,
- screen_info_fixup_lfb);
static struct pci_dev *__screen_info_pci_dev(struct resource *res)
{
diff --git a/include/linux/screen_info.h b/include/linux/screen_info.h
index c022403c599a..2adbe25b88d8 100644
--- a/include/linux/screen_info.h
+++ b/include/linux/screen_info.h
@@ -140,11 +140,8 @@ u32 __screen_info_lfb_bits_per_pixel(const struct screen_info *si);
int screen_info_pixel_format(const struct screen_info *si, struct pixel_format *f);
#if defined(CONFIG_PCI)
-void screen_info_apply_fixups(void);
struct pci_dev *screen_info_pci_dev(const struct screen_info *si);
#else
-static inline void screen_info_apply_fixups(void)
-{ }
static inline struct pci_dev *screen_info_pci_dev(const struct screen_info *si)
{
return NULL;
--
2.53.0
^ permalink raw reply related
* [PATCH 8/8] firmware: sysfb: Move CONFIG_FIRMWARE_EDID to firmware options
From: Thomas Zimmermann @ 2026-04-02 9:09 UTC (permalink / raw)
To: javierm, arnd, ardb, ilias.apalodimas, chenhuacai, kernel,
maarten.lankhorst, mripard, airlied, simona, kys, haiyangz,
wei.liu, decui, longli, deller
Cc: linux-arm-kernel, loongarch, linux-efi, linux-riscv, dri-devel,
linux-hyperv, linux-fbdev, Thomas Zimmermann
In-Reply-To: <20260402092305.208728-1-tzimmermann@suse.de>
Move the Kconfig option for CONFIG_FIRMWARE_EDID to the firmware
subsystem. The option controls architecture and firmware code, so
it fits here better than in video.
Also make it depend on CONFIG_SYSFB. The EDID data is stored in
sysfb_primary_display and only useful with a sysfb framebuffer. This
further allows for removing an explicit test for CONFIG_FIRMWARE_EDID
from the EFI init code. For loongson, select CONFIG_SYSFB in the
defconfig files.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
arch/loongarch/configs/loongson32_defconfig | 1 +
arch/loongarch/configs/loongson64_defconfig | 1 +
drivers/firmware/Kconfig | 20 ++++++++++++++++++++
drivers/firmware/efi/efi-init.c | 2 +-
drivers/video/Kconfig | 19 -------------------
5 files changed, 23 insertions(+), 20 deletions(-)
diff --git a/arch/loongarch/configs/loongson32_defconfig b/arch/loongarch/configs/loongson32_defconfig
index 276b1577e0be..1c0897723247 100644
--- a/arch/loongarch/configs/loongson32_defconfig
+++ b/arch/loongarch/configs/loongson32_defconfig
@@ -786,6 +786,7 @@ CONFIG_DRM_VIRTIO_GPU=m
CONFIG_DRM_LOONGSON=y
CONFIG_FB=y
CONFIG_FB_RADEON=y
+CONFIG_SYSFB=y
CONFIG_FIRMWARE_EDID=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_PLATFORM=m
diff --git a/arch/loongarch/configs/loongson64_defconfig b/arch/loongarch/configs/loongson64_defconfig
index a14db1a95e7e..38340537dfd4 100644
--- a/arch/loongarch/configs/loongson64_defconfig
+++ b/arch/loongarch/configs/loongson64_defconfig
@@ -816,6 +816,7 @@ CONFIG_DRM_VIRTIO_GPU=m
CONFIG_DRM_LOONGSON=y
CONFIG_FB=y
CONFIG_FB_RADEON=y
+CONFIG_SYSFB=y
CONFIG_FIRMWARE_EDID=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_PLATFORM=m
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 52f8253a46b1..edfb171d9eab 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -225,6 +225,26 @@ config SYSFB_SIMPLEFB
If unsure, say Y.
+config FIRMWARE_EDID
+ bool "Enable firmware EDID"
+ depends on SYSFB
+ depends on EFI_GENERIC_STUB || X86
+ help
+ This enables access to the EDID transferred from the firmware.
+ On EFI systems, the EDID comes from the same device as the
+ primary GOP. On x86 with BIOS, it comes from the VESA BIOS.
+ DRM display drivers will be able to export the information
+ to userspace.
+
+ Also enable this if DDC/I2C transfers do not work for your driver
+ and if you are using nvidiafb, i810fb or savagefb.
+
+ In general, choosing Y for this option is safe. If you
+ experience extremely long delays while booting before you get
+ something on your display, try setting this to N. Matrox cards in
+ combination with certain motherboards and monitors are known to
+ suffer from this problem.
+
config TH1520_AON_PROTOCOL
tristate "Always-On firmware protocol"
depends on ARCH_THEAD || COMPILE_TEST
diff --git a/drivers/firmware/efi/efi-init.c b/drivers/firmware/efi/efi-init.c
index 002518b642ed..c4088fb8482b 100644
--- a/drivers/firmware/efi/efi-init.c
+++ b/drivers/firmware/efi/efi-init.c
@@ -60,7 +60,7 @@ extern __weak const efi_config_table_type_t efi_arch_tables[];
* x86 defines its own instance of sysfb_primary_display and uses
* it even without EFI, everything else can get them from here.
*/
-#if !defined(CONFIG_X86) && (defined(CONFIG_SYSFB) || defined(CONFIG_FIRMWARE_EDID))
+#if !defined(CONFIG_X86) && defined(CONFIG_SYSFB)
struct sysfb_display_info sysfb_primary_display __section(".data");
EXPORT_SYMBOL_GPL(sysfb_primary_display);
#endif
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index a7144d275f54..1c9ac3b029a7 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -62,25 +62,6 @@ config HDMI
endif # HAS_IOMEM
-config FIRMWARE_EDID
- bool "Enable firmware EDID"
- depends on EFI_GENERIC_STUB || X86
- help
- This enables access to the EDID transferred from the firmware.
- On EFI systems, the EDID comes from the same device as the
- primary GOP. On x86 with BIOS, it comes from the VESA BIOS.
- DRM display drivers will be able to export the information
- to userspace.
-
- Also enable this if DDC/I2C transfers do not work for your driver
- and if you are using nvidiafb, i810fb or savagefb.
-
- In general, choosing Y for this option is safe. If you
- experience extremely long delays while booting before you get
- something on your display, try setting this to N. Matrox cards in
- combination with certain motherboards and monitors are known to
- suffer from this problem.
-
if VT
source "drivers/video/console/Kconfig"
endif
--
2.53.0
^ permalink raw reply related
* [PATCH 3/8] firmware: sysfb: Make CONFIG_SYSFB a user-selectable option
From: Thomas Zimmermann @ 2026-04-02 9:09 UTC (permalink / raw)
To: javierm, arnd, ardb, ilias.apalodimas, chenhuacai, kernel,
maarten.lankhorst, mripard, airlied, simona, kys, haiyangz,
wei.liu, decui, longli, deller
Cc: linux-arm-kernel, loongarch, linux-efi, linux-riscv, dri-devel,
linux-hyperv, linux-fbdev, Thomas Zimmermann
In-Reply-To: <20260402092305.208728-1-tzimmermann@suse.de>
Add a descriptive string and help text to CONFIG_SYSFB, so that users
can modify it. Flip all implicit selects in the Kconfig options into
dependencies. This avoids cyclic dependencies in the config.
Enabling CONFIG_SYSFB makes the kernel provide a device for the firmware
framebuffer. As this can (slightly) affect system behavior, having a
user-facing option seems preferable. Some users might also want to set
every detail of their kernel config.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/firmware/Kconfig | 18 ++++++++++++++++--
drivers/gpu/drm/sysfb/Kconfig | 4 ++--
drivers/hv/Kconfig | 2 +-
drivers/video/fbdev/Kconfig | 5 +++--
4 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index bbd2155d8483..52f8253a46b1 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -179,14 +179,28 @@ config MTK_ADSP_IPC
Client might use shared memory to exchange information with ADSP.
config SYSFB
- bool
+ bool "Enable system framebuffer provided by boot loader"
select BOOT_VESA_SUPPORT
select SCREEN_INFO
+ help
+ Use the system framebuffer provided by the boot loader. This will
+ create a device representing the framebuffer. The output depends on
+ EFI, VESA, VGA, or some other firmware-based interface.
+
+ The firmware or boot loader sets the display resolution and color
+ mode. See your boot loader's documentation on how to do this. On
+ some systems the display can also be configured during boot with
+ the kernel's video= or vga= parameters.
+
+ Besides this option, you also have to enable a compatible graphics
+ driver, such as efidrm or vesadrm.
+
+ If unsure, say Y.
config SYSFB_SIMPLEFB
bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
+ depends on SYSFB
depends on X86 || EFI
- select SYSFB
help
Firmwares often provide initial graphics framebuffers so the BIOS,
bootloader or kernel can show basic video-output during boot for
diff --git a/drivers/gpu/drm/sysfb/Kconfig b/drivers/gpu/drm/sysfb/Kconfig
index 2559ead6cf1f..74be3c8e6657 100644
--- a/drivers/gpu/drm/sysfb/Kconfig
+++ b/drivers/gpu/drm/sysfb/Kconfig
@@ -26,12 +26,12 @@ config DRM_COREBOOTDRM
config DRM_EFIDRM
tristate "EFI framebuffer driver"
depends on DRM && MMU && EFI && (!SYSFB_SIMPLEFB || COMPILE_TEST)
+ depends on SYSFB
select APERTURE_HELPERS
select DRM_CLIENT_SELECTION
select DRM_GEM_SHMEM_HELPER
select DRM_KMS_HELPER
select DRM_SYSFB_HELPER
- select SYSFB
help
DRM driver for EFI framebuffers.
@@ -76,12 +76,12 @@ config DRM_SIMPLEDRM
config DRM_VESADRM
tristate "VESA framebuffer driver"
depends on DRM && MMU && X86 && (!SYSFB_SIMPLEFB || COMPILE_TEST)
+ depends on SYSFB
select APERTURE_HELPERS
select DRM_CLIENT_SELECTION
select DRM_GEM_SHMEM_HELPER
select DRM_KMS_HELPER
select DRM_SYSFB_HELPER
- select SYSFB
help
DRM driver for VESA framebuffers.
diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
index 2d0b3fcb0ff8..af0ac6516159 100644
--- a/drivers/hv/Kconfig
+++ b/drivers/hv/Kconfig
@@ -60,8 +60,8 @@ config HYPERV_BALLOON
config HYPERV_VMBUS
tristate "Microsoft Hyper-V VMBus driver"
depends on HYPERV
+ depends on SYSFB if EFI && !HYPERV_VTL_MODE
default HYPERV
- select SYSFB if EFI && !HYPERV_VTL_MODE
help
Select this option to enable Hyper-V Vmbus driver.
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index ac9ac4287c6a..6f55bec8c207 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -333,6 +333,7 @@ config FB_IMSTT
config FB_VGA16
tristate "VGA 16-color graphics support"
depends on FB && X86
+ depends on SYSFB
select APERTURE_HELPERS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
@@ -424,9 +425,9 @@ config FB_UVESA
config FB_VESA
bool "VESA VGA graphics support"
depends on (FB = y) && X86
+ depends on SYSFB
select APERTURE_HELPERS
select FB_IOMEM_HELPERS
- select SYSFB
help
This is the frame buffer device driver for generic VESA 2.0
compliant graphic cards. The older VESA 1.2 cards are not supported.
@@ -436,10 +437,10 @@ config FB_VESA
config FB_EFI
bool "EFI-based Framebuffer Support"
depends on (FB = y) && EFI
+ depends on SYSFB
select APERTURE_HELPERS
select DRM_PANEL_ORIENTATION_QUIRKS
select FB_IOMEM_HELPERS
- select SYSFB
help
This is the EFI frame buffer device driver. If the firmware on
your platform is EFI 1.10 or UEFI 2.0, select Y to add support for
--
2.53.0
^ permalink raw reply related
* [PATCH 7/8] firmware: efi: Make CONFIG_EFI_EARLYCON depend on CONFIG_SYSFB; clean up
From: Thomas Zimmermann @ 2026-04-02 9:09 UTC (permalink / raw)
To: javierm, arnd, ardb, ilias.apalodimas, chenhuacai, kernel,
maarten.lankhorst, mripard, airlied, simona, kys, haiyangz,
wei.liu, decui, longli, deller
Cc: linux-arm-kernel, loongarch, linux-efi, linux-riscv, dri-devel,
linux-hyperv, linux-fbdev, Thomas Zimmermann
In-Reply-To: <20260402092305.208728-1-tzimmermann@suse.de>
Efi-earlycon uses sysfb_primary_display. Therefore make it depend on
the corresponding config symbol. With this in place, go through the
source files and reduce tests to CONFIG_SYSFB. Efi-earlycon is now
just another regular user of sysfb.
This also enables the screen_info relocation feature for efi-earlycon.
Systems might move the framebuffer aperture while booting the kernel. PCI
bridges sometimes do this as part of relocating the sub-bus aperture.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
arch/arm64/kernel/image-vars.h | 2 +-
arch/loongarch/kernel/efi.c | 4 ++--
arch/loongarch/kernel/image-vars.h | 2 +-
arch/riscv/kernel/image-vars.h | 2 +-
drivers/firmware/efi/Kconfig | 3 ++-
drivers/firmware/efi/efi-init.c | 6 ++----
drivers/firmware/efi/libstub/efi-stub-entry.c | 4 +---
7 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h
index d7b0d12b1015..7e0e61385286 100644
--- a/arch/arm64/kernel/image-vars.h
+++ b/arch/arm64/kernel/image-vars.h
@@ -37,7 +37,7 @@ PROVIDE(__efistub__text = _text);
PROVIDE(__efistub__end = _end);
PROVIDE(__efistub___inittext_end = __inittext_end);
PROVIDE(__efistub__edata = _edata);
-#if defined(CONFIG_EFI_EARLYCON) || defined(CONFIG_SYSFB)
+#if defined(CONFIG_SYSFB)
PROVIDE(__efistub_sysfb_primary_display = sysfb_primary_display);
#endif
PROVIDE(__efistub__ctype = _ctype);
diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c
index 69dd83f8082f..6c25e2ecf45f 100644
--- a/arch/loongarch/kernel/efi.c
+++ b/arch/loongarch/kernel/efi.c
@@ -74,7 +74,7 @@ bool efi_poweroff_required(void)
unsigned long __initdata primary_display_table = EFI_INVALID_TABLE_ADDR;
-#if defined(CONFIG_SYSFB) || defined(CONFIG_EFI_EARLYCON)
+#if defined(CONFIG_SYSFB)
struct sysfb_display_info sysfb_primary_display __section(".data");
EXPORT_SYMBOL_GPL(sysfb_primary_display);
#endif
@@ -129,7 +129,7 @@ void __init efi_init(void)
set_bit(EFI_CONFIG_TABLES, &efi.flags);
- if (IS_ENABLED(CONFIG_EFI_EARLYCON) || IS_ENABLED(CONFIG_SYSFB))
+ if (IS_ENABLED(CONFIG_SYSFB))
init_primary_display();
if (boot_memmap == EFI_INVALID_TABLE_ADDR)
diff --git a/arch/loongarch/kernel/image-vars.h b/arch/loongarch/kernel/image-vars.h
index e557ebd46c2b..c43f9326f344 100644
--- a/arch/loongarch/kernel/image-vars.h
+++ b/arch/loongarch/kernel/image-vars.h
@@ -11,7 +11,7 @@ __efistub_strcmp = strcmp;
__efistub_kernel_entry = kernel_entry;
__efistub_kernel_asize = kernel_asize;
__efistub_kernel_fsize = kernel_fsize;
-#if defined(CONFIG_EFI_EARLYCON) || defined(CONFIG_SYSFB)
+#if defined(CONFIG_SYSFB)
__efistub_sysfb_primary_display = sysfb_primary_display;
#endif
diff --git a/arch/riscv/kernel/image-vars.h b/arch/riscv/kernel/image-vars.h
index 3bd9d06a8b8f..3ab2529c4154 100644
--- a/arch/riscv/kernel/image-vars.h
+++ b/arch/riscv/kernel/image-vars.h
@@ -28,7 +28,7 @@ __efistub__start_kernel = _start_kernel;
__efistub__end = _end;
__efistub__edata = _edata;
__efistub___init_text_end = __init_text_end;
-#if defined(CONFIG_EFI_EARLYCON) || defined(CONFIG_SYSFB)
+#if defined(CONFIG_SYSFB)
__efistub_sysfb_primary_display = sysfb_primary_display;
#endif
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index 29e0729299f5..925ded080eb4 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -219,8 +219,9 @@ config EFI_DISABLE_PCI_DMA
config EFI_EARLYCON
def_bool y
depends on SERIAL_EARLYCON && !ARM
- select FONT_SUPPORT
+ depends on SYSFB
select ARCH_USE_MEMREMAP_PROT
+ select FONT_SUPPORT
config EFI_CUSTOM_SSDT_OVERLAYS
bool "Load custom ACPI SSDT overlay from an EFI variable"
diff --git a/drivers/firmware/efi/efi-init.c b/drivers/firmware/efi/efi-init.c
index 6103b1a082d2..002518b642ed 100644
--- a/drivers/firmware/efi/efi-init.c
+++ b/drivers/firmware/efi/efi-init.c
@@ -60,7 +60,7 @@ extern __weak const efi_config_table_type_t efi_arch_tables[];
* x86 defines its own instance of sysfb_primary_display and uses
* it even without EFI, everything else can get them from here.
*/
-#if !defined(CONFIG_X86) && (defined(CONFIG_SYSFB) || defined(CONFIG_EFI_EARLYCON) || defined(CONFIG_FIRMWARE_EDID))
+#if !defined(CONFIG_X86) && (defined(CONFIG_SYSFB) || defined(CONFIG_FIRMWARE_EDID))
struct sysfb_display_info sysfb_primary_display __section(".data");
EXPORT_SYMBOL_GPL(sysfb_primary_display);
#endif
@@ -271,8 +271,6 @@ void __init efi_init(void)
memblock_reserve(data.phys_map & PAGE_MASK,
PAGE_ALIGN(data.size + (data.phys_map & ~PAGE_MASK)));
- if (IS_ENABLED(CONFIG_X86) ||
- IS_ENABLED(CONFIG_SYSFB) ||
- IS_ENABLED(CONFIG_EFI_EARLYCON))
+ if (IS_ENABLED(CONFIG_X86) || IS_ENABLED(CONFIG_SYSFB))
init_primary_display();
}
diff --git a/drivers/firmware/efi/libstub/efi-stub-entry.c b/drivers/firmware/efi/libstub/efi-stub-entry.c
index aa85e910fe59..214fa4d84df0 100644
--- a/drivers/firmware/efi/libstub/efi-stub-entry.c
+++ b/drivers/firmware/efi/libstub/efi-stub-entry.c
@@ -19,9 +19,7 @@ struct sysfb_display_info *alloc_primary_display(void)
if (IS_ENABLED(CONFIG_ARM))
return __alloc_primary_display();
- if (IS_ENABLED(CONFIG_X86) ||
- IS_ENABLED(CONFIG_EFI_EARLYCON) ||
- IS_ENABLED(CONFIG_SYSFB))
+ if (IS_ENABLED(CONFIG_X86) || IS_ENABLED(CONFIG_SYSFB))
return kernel_image_addr(&sysfb_primary_display);
return NULL;
--
2.53.0
^ permalink raw reply related
* [PATCH 2/8] firmware: efi: Never declare sysfb_primary_display on x86
From: Thomas Zimmermann @ 2026-04-02 9:09 UTC (permalink / raw)
To: javierm, arnd, ardb, ilias.apalodimas, chenhuacai, kernel,
maarten.lankhorst, mripard, airlied, simona, kys, haiyangz,
wei.liu, decui, longli, deller
Cc: linux-arm-kernel, loongarch, linux-efi, linux-riscv, dri-devel,
linux-hyperv, linux-fbdev, Thomas Zimmermann, kernel test robot
In-Reply-To: <20260402092305.208728-1-tzimmermann@suse.de>
The x86 architecture comes with its own instance of the global
state variable sysfb_primary_display. Never declare it in the EFI
subsystem. Fix the test for CONFIG_FIRMWARE_EDID accordingly.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: e65ca1646311 ("efi: export sysfb_primary_display for EDID")
Cc: kernel test robot <lkp@intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Cc: linux-efi@vger.kernel.org
---
drivers/firmware/efi/efi-init.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/firmware/efi/efi-init.c b/drivers/firmware/efi/efi-init.c
index 5a595d026f58..6103b1a082d2 100644
--- a/drivers/firmware/efi/efi-init.c
+++ b/drivers/firmware/efi/efi-init.c
@@ -60,7 +60,7 @@ extern __weak const efi_config_table_type_t efi_arch_tables[];
* x86 defines its own instance of sysfb_primary_display and uses
* it even without EFI, everything else can get them from here.
*/
-#if !defined(CONFIG_X86) && (defined(CONFIG_SYSFB) || defined(CONFIG_EFI_EARLYCON)) || defined(CONFIG_FIRMWARE_EDID)
+#if !defined(CONFIG_X86) && (defined(CONFIG_SYSFB) || defined(CONFIG_EFI_EARLYCON) || defined(CONFIG_FIRMWARE_EDID))
struct sysfb_display_info sysfb_primary_display __section(".data");
EXPORT_SYMBOL_GPL(sysfb_primary_display);
#endif
--
2.53.0
^ permalink raw reply related
* [PATCH 6/8] firmware: sysfb: Avoid forward-declaring sysfb_parent_dev()
From: Thomas Zimmermann @ 2026-04-02 9:09 UTC (permalink / raw)
To: javierm, arnd, ardb, ilias.apalodimas, chenhuacai, kernel,
maarten.lankhorst, mripard, airlied, simona, kys, haiyangz,
wei.liu, decui, longli, deller
Cc: linux-arm-kernel, loongarch, linux-efi, linux-riscv, dri-devel,
linux-hyperv, linux-fbdev, Thomas Zimmermann
In-Reply-To: <20260402092305.208728-1-tzimmermann@suse.de>
Move sysfb_parent_dev() to the top of the source file to avoid
the extra declaration. No functional changes.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/firmware/sysfb_primary.c | 36 +++++++++++++++-----------------
1 file changed, 17 insertions(+), 19 deletions(-)
diff --git a/drivers/firmware/sysfb_primary.c b/drivers/firmware/sysfb_primary.c
index 298f87a43a7e..a7f8cede60ad 100644
--- a/drivers/firmware/sysfb_primary.c
+++ b/drivers/firmware/sysfb_primary.c
@@ -42,7 +42,23 @@ static struct platform_device *pd;
static DEFINE_MUTEX(disable_lock);
static bool disabled;
-static struct device *sysfb_parent_dev(const struct screen_info *si);
+static struct device *sysfb_parent_dev(const struct screen_info *si)
+{
+ struct pci_dev *pdev;
+
+ pdev = screen_info_pci_dev(si);
+ if (IS_ERR(pdev)) {
+ return ERR_CAST(pdev);
+ } else if (pdev) {
+ if (!sysfb_pci_dev_is_enabled(pdev)) {
+ pci_dev_put(pdev);
+ return ERR_PTR(-ENODEV);
+ }
+ return &pdev->dev;
+ }
+
+ return NULL;
+}
static bool sysfb_unregister(void)
{
@@ -101,24 +117,6 @@ bool sysfb_handles_screen_info(void)
}
EXPORT_SYMBOL_GPL(sysfb_handles_screen_info);
-static struct device *sysfb_parent_dev(const struct screen_info *si)
-{
- struct pci_dev *pdev;
-
- pdev = screen_info_pci_dev(si);
- if (IS_ERR(pdev)) {
- return ERR_CAST(pdev);
- } else if (pdev) {
- if (!sysfb_pci_dev_is_enabled(pdev)) {
- pci_dev_put(pdev);
- return ERR_PTR(-ENODEV);
- }
- return &pdev->dev;
- }
-
- return NULL;
-}
-
static __init int sysfb_init(void)
{
struct sysfb_display_info *dpy = &sysfb_primary_display;
--
2.53.0
^ permalink raw reply related
* [PATCH 1/8] hv: Select CONFIG_SYSFB only for CONFIG_HYPERV_VMBUS
From: Thomas Zimmermann @ 2026-04-02 9:09 UTC (permalink / raw)
To: javierm, arnd, ardb, ilias.apalodimas, chenhuacai, kernel,
maarten.lankhorst, mripard, airlied, simona, kys, haiyangz,
wei.liu, decui, longli, deller
Cc: linux-arm-kernel, loongarch, linux-efi, linux-riscv, dri-devel,
linux-hyperv, linux-fbdev, Thomas Zimmermann, Michael Kelley,
Saurabh Sengar, stable
In-Reply-To: <20260402092305.208728-1-tzimmermann@suse.de>
Hyperv's sysfb access only exists in the VMBUS support. Therefore
only select CONFIG_SYSFB for CONFIG_HYPERV_VMBUS. Avoids sysfb code
on systems that don't need it.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 96959283a58d ("Drivers: hv: Always select CONFIG_SYSFB for Hyper-V guests")
Cc: Michael Kelley <mhklinux@outlook.com>
Cc: Saurabh Sengar <ssengar@linux.microsoft.com>
Cc: Wei Liu <wei.liu@kernel.org>
Cc: "K. Y. Srinivasan" <kys@microsoft.com>
Cc: Haiyang Zhang <haiyangz@microsoft.com>
Cc: Dexuan Cui <decui@microsoft.com>
Cc: Long Li <longli@microsoft.com>
Cc: linux-hyperv@vger.kernel.org
Cc: <stable@vger.kernel.org> # v6.16+
---
drivers/hv/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
index 7937ac0cbd0f..2d0b3fcb0ff8 100644
--- a/drivers/hv/Kconfig
+++ b/drivers/hv/Kconfig
@@ -9,7 +9,6 @@ config HYPERV
select PARAVIRT
select X86_HV_CALLBACK_VECTOR if X86
select OF_EARLY_FLATTREE if OF
- select SYSFB if EFI && !HYPERV_VTL_MODE
select IRQ_MSI_LIB if X86
help
Select this option to run Linux as a Hyper-V client operating
@@ -62,6 +61,7 @@ config HYPERV_VMBUS
tristate "Microsoft Hyper-V VMBus driver"
depends on HYPERV
default HYPERV
+ select SYSFB if EFI && !HYPERV_VTL_MODE
help
Select this option to enable Hyper-V Vmbus driver.
--
2.53.0
^ permalink raw reply related
* [PATCH 4/8] firmware: sysfb: Split sysfb.c into sysfb_primary.c and sysfb_pci.c
From: Thomas Zimmermann @ 2026-04-02 9:09 UTC (permalink / raw)
To: javierm, arnd, ardb, ilias.apalodimas, chenhuacai, kernel,
maarten.lankhorst, mripard, airlied, simona, kys, haiyangz,
wei.liu, decui, longli, deller
Cc: linux-arm-kernel, loongarch, linux-efi, linux-riscv, dri-devel,
linux-hyperv, linux-fbdev, Thomas Zimmermann
In-Reply-To: <20260402092305.208728-1-tzimmermann@suse.de>
Move the init code for the primary graphics device and the PCI-helpers
into separate source files. Only build the PCI helpers if CONFIG_PCI is
set. Prepares sysfb for additional PCI helpers.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/firmware/Makefile | 7 ++++--
drivers/firmware/sysfb.h | 19 ++++++++++++++
drivers/firmware/sysfb_pci.c | 21 ++++++++++++++++
drivers/firmware/{sysfb.c => sysfb_primary.c} | 25 ++-----------------
4 files changed, 47 insertions(+), 25 deletions(-)
create mode 100644 drivers/firmware/sysfb.h
create mode 100644 drivers/firmware/sysfb_pci.c
rename drivers/firmware/{sysfb.c => sysfb_primary.c} (92%)
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 4ddec2820c96..5b0592c078df 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -16,13 +16,16 @@ obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o
obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o
-obj-$(CONFIG_SYSFB) += sysfb.o
-obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o
obj-$(CONFIG_TH1520_AON_PROTOCOL) += thead,th1520-aon.o
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o
obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o
+sysfb-y := sysfb_primary.o
+sysfb-$(CONFIG_PCI) += sysfb_pci.o
+sysfb-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o
+obj-$(CONFIG_SYSFB) += sysfb.o
+
obj-y += arm_ffa/
obj-y += arm_scmi/
obj-y += broadcom/
diff --git a/drivers/firmware/sysfb.h b/drivers/firmware/sysfb.h
new file mode 100644
index 000000000000..9f7fe2e03f68
--- /dev/null
+++ b/drivers/firmware/sysfb.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef FIRMWARE_SYSFB_H
+#define FIRMWARE_SYSFB_H
+
+#include <linux/types.h>
+
+struct pci_dev;
+
+#ifdef CONFIG_PCI
+bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev);
+#else
+static inline bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
+{
+ return false;
+}
+#endif
+
+#endif
diff --git a/drivers/firmware/sysfb_pci.c b/drivers/firmware/sysfb_pci.c
new file mode 100644
index 000000000000..8f3adeef4fb1
--- /dev/null
+++ b/drivers/firmware/sysfb_pci.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/pci.h>
+
+#include "sysfb.h"
+
+bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
+{
+ /*
+ * TODO: Try to integrate this code into the PCI subsystem
+ */
+ int ret;
+ u16 command;
+
+ ret = pci_read_config_word(pdev, PCI_COMMAND, &command);
+ if (ret != PCIBIOS_SUCCESSFUL)
+ return false;
+ if (!(command & PCI_COMMAND_MEMORY))
+ return false;
+ return true;
+}
diff --git a/drivers/firmware/sysfb.c b/drivers/firmware/sysfb_primary.c
similarity index 92%
rename from drivers/firmware/sysfb.c
rename to drivers/firmware/sysfb_primary.c
index 8833582c1883..ab8d7fc468bb 100644
--- a/drivers/firmware/sysfb.c
+++ b/drivers/firmware/sysfb_primary.c
@@ -35,6 +35,8 @@
#include <linux/screen_info.h>
#include <linux/sysfb.h>
+#include "sysfb.h"
+
static struct platform_device *pd;
static DEFINE_MUTEX(disable_lock);
static bool disabled;
@@ -98,29 +100,6 @@ bool sysfb_handles_screen_info(void)
}
EXPORT_SYMBOL_GPL(sysfb_handles_screen_info);
-#if defined(CONFIG_PCI)
-static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
-{
- /*
- * TODO: Try to integrate this code into the PCI subsystem
- */
- int ret;
- u16 command;
-
- ret = pci_read_config_word(pdev, PCI_COMMAND, &command);
- if (ret != PCIBIOS_SUCCESSFUL)
- return false;
- if (!(command & PCI_COMMAND_MEMORY))
- return false;
- return true;
-}
-#else
-static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
-{
- return false;
-}
-#endif
-
static struct device *sysfb_parent_dev(const struct screen_info *si)
{
struct pci_dev *pdev;
--
2.53.0
^ permalink raw reply related
* [PATCH 0/8] firmware: sysfb: Consolidate config/code wrt. sysfb_primary_screen
From: Thomas Zimmermann @ 2026-04-02 9:09 UTC (permalink / raw)
To: javierm, arnd, ardb, ilias.apalodimas, chenhuacai, kernel,
maarten.lankhorst, mripard, airlied, simona, kys, haiyangz,
wei.liu, decui, longli, deller
Cc: linux-arm-kernel, loongarch, linux-efi, linux-riscv, dri-devel,
linux-hyperv, linux-fbdev, Thomas Zimmermann
The global state sysfb_primary_screen holds information about the
framebuffer provided by EFI/BIOS systems. It is part of the sysfb
module, but used in several places without direct connection to
sysfb. Fix this by making users of sysfb_primary_screen depend on
CONFIG_SYSFB. Fix a few issues in the process.
Patches 1 and 2 fix general errors in the Kconfig rules. In any case,
these patches should be considered even without the rest of the series.
Patch 3 makes CONFIG_SYSFB a user-controlled option, which gives users
more control over the configuration.
Patches 4 to 6 slightly refactor the sysfb code. The PCI helpers are
now in a separate source file. Support for relocating the PCI framebuffer
is now located in sysfb, where it belongs.
Patches 7 and 8 make earlycon and the firmware EDID depend on sysfb. Both
rely on sysfb_primary_display to work correctly.
Smoke-tested with bochs on qemu. Built with/without CONFIG_SYSFB enabled.
Note: vgaarb still guards sysfb_primary_display with CONFIG_X86 instead
of CONFIG_SYSFB. That works reliably and is left in place, as a change to
guard with SYSFB_SYSFB might affect non-x86 systems in unknown ways.
Thomas Zimmermann (8):
hv: Select CONFIG_SYSFB only for CONFIG_HYPERV_VMBUS
firmware: efi: Never declare sysfb_primary_display on x86
firmware: sysfb: Make CONFIG_SYSFB a user-selectable option
firmware: sysfb: Split sysfb.c into sysfb_primary.c and sysfb_pci.c
firmware: sysfb: Implement screen_info relocation for primary display
firmware: sysfb: Avoid forward-declaring sysfb_parent_dev()
firmware: efi: Make CONFIG_EFI_EARLYCON depend on CONFIG_SYSFB; clean
up
firmware: sysfb: Move CONFIG_FIRMWARE_EDID to firmware options
arch/arm64/kernel/image-vars.h | 2 +-
arch/loongarch/configs/loongson32_defconfig | 1 +
arch/loongarch/configs/loongson64_defconfig | 1 +
arch/loongarch/kernel/efi.c | 4 +-
arch/loongarch/kernel/image-vars.h | 2 +-
arch/riscv/kernel/image-vars.h | 2 +-
drivers/firmware/Kconfig | 38 ++++-
drivers/firmware/Makefile | 7 +-
drivers/firmware/efi/Kconfig | 3 +-
drivers/firmware/efi/efi-init.c | 6 +-
drivers/firmware/efi/libstub/efi-stub-entry.c | 4 +-
drivers/firmware/sysfb.h | 24 ++++
drivers/firmware/sysfb_pci.c | 132 ++++++++++++++++++
drivers/firmware/{sysfb.c => sysfb_primary.c} | 70 ++++------
drivers/gpu/drm/sysfb/Kconfig | 4 +-
drivers/hv/Kconfig | 2 +-
drivers/video/Kconfig | 19 ---
drivers/video/fbdev/Kconfig | 5 +-
drivers/video/screen_info_pci.c | 110 ---------------
include/linux/screen_info.h | 3 -
20 files changed, 241 insertions(+), 198 deletions(-)
create mode 100644 drivers/firmware/sysfb.h
create mode 100644 drivers/firmware/sysfb_pci.c
rename drivers/firmware/{sysfb.c => sysfb_primary.c} (90%)
base-commit: 5d36e6d54e963f0c1137aaf2249d2baa781f08c2
--
2.53.0
^ permalink raw reply
* Re: [PATCH v1 01/27] VFIO: take reference to the KVM module
From: Paolo Bonzini @ 2026-04-02 9:18 UTC (permalink / raw)
To: Steffen Eiden, kvm, kvmarm, linux-arm-kernel, linux-kernel,
linux-s390
Cc: Andreas Grapentin, Arnd Bergmann, Catalin Marinas,
Christian Borntraeger, Claudio Imbrenda, David Hildenbrand,
Gautam Gala, Hendrik Brueckner, Janosch Frank, Joey Gouly,
Marc Zyngier, Nina Schoetterl-Glausch, Oliver Upton,
Suzuki K Poulose, Ulrich Weigand, Will Deacon, Zenghui Yu
In-Reply-To: <20260402042125.3948963-2-seiden@linux.ibm.com>
On 4/2/26 06:20, Steffen Eiden wrote:
> @@ -157,6 +158,7 @@ static int kvm_vfio_file_add(struct kvm_device *dev, unsigned int fd)
> goto out_fput;
> }
>
> + module = filp->f_op->owner;
This patch is incorrect because filp->f_op->owner is actually the VFIO
module.
I'll send a replacement series for the first three patches here.
Paolo
^ permalink raw reply
* Re: [PATCH v6 02/10] reset: Add Realtek basic reset support
From: Philipp Zabel @ 2026-04-02 9:15 UTC (permalink / raw)
To: Yu-Chun Lin, mturquette, sboyd, robh, krzk+dt, conor+dt, cylee12,
afaerber, jyanchou
Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
linux-realtek-soc, james.tai, cy.huang, stanley_chang
In-Reply-To: <20260402073957.2742459-3-eleanor.lin@realtek.com>
On Do, 2026-04-02 at 15:39 +0800, Yu-Chun Lin wrote:
> From: Cheng-Yu Lee <cylee12@realtek.com>
>
> Define the reset operations backed by a regmap-based register interface
> and prepare the reset controller to be registered through the reset
> framework.
>
> Since the reset controllers on Realtek SoCs often share the same register
> space with the clock controllers, this common framework is designed to
> extract the regmap and device tree node from the parent device
> (e.g., an auxiliary device parent).
>
> Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
> Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> ---
> Changes in v6:
> - Remove the global header include/linux/reset/realtek.h and use a local common.h
> instead.
> - Extract regmap and of_node directly from the parent device.
> - Remove struct rtk_reset_initdata. Now, pass struct rtk_reset_data directly when
> calling rtk_reset_controller_add().
> ---
> MAINTAINERS | 1 +
> drivers/reset/Kconfig | 1 +
> drivers/reset/Makefile | 1 +
> drivers/reset/realtek/Kconfig | 3 ++
> drivers/reset/realtek/Makefile | 2 +
> drivers/reset/realtek/common.c | 85 ++++++++++++++++++++++++++++++++++
> drivers/reset/realtek/common.h | 29 ++++++++++++
> 7 files changed, 122 insertions(+)
> create mode 100644 drivers/reset/realtek/Kconfig
> create mode 100644 drivers/reset/realtek/Makefile
> create mode 100644 drivers/reset/realtek/common.c
> create mode 100644 drivers/reset/realtek/common.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 07e73bf621b0..8f355896583b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22240,6 +22240,7 @@ L: devicetree@vger.kernel.org
> L: linux-clk@vger.kernel.org
> S: Supported
> F: Documentation/devicetree/bindings/clock/realtek*
> +F: drivers/reset/realtek/*
> F: include/dt-bindings/clock/realtek*
> F: include/dt-bindings/reset/realtek*
>
> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
> index 7ce151f6a7e4..03be1931f264 100644
> --- a/drivers/reset/Kconfig
> +++ b/drivers/reset/Kconfig
> @@ -398,6 +398,7 @@ config RESET_ZYNQMP
>
> source "drivers/reset/amlogic/Kconfig"
> source "drivers/reset/hisilicon/Kconfig"
> +source "drivers/reset/realtek/Kconfig"
> source "drivers/reset/spacemit/Kconfig"
> source "drivers/reset/starfive/Kconfig"
> source "drivers/reset/sti/Kconfig"
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index fc0cc99f8514..4407d1630070 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -2,6 +2,7 @@
> obj-y += core.o
> obj-y += amlogic/
> obj-y += hisilicon/
> +obj-y += realtek/
> obj-y += spacemit/
> obj-y += starfive/
> obj-y += sti/
> diff --git a/drivers/reset/realtek/Kconfig b/drivers/reset/realtek/Kconfig
> new file mode 100644
> index 000000000000..99a14d355803
> --- /dev/null
> +++ b/drivers/reset/realtek/Kconfig
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config RESET_RTK_COMMON
> + bool
Please make this build-testable with COMPILE_TEST.
> diff --git a/drivers/reset/realtek/Makefile b/drivers/reset/realtek/Makefile
> new file mode 100644
> index 000000000000..b59a3f7f2453
> --- /dev/null
> +++ b/drivers/reset/realtek/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_RESET_RTK_COMMON) += common.o
> diff --git a/drivers/reset/realtek/common.c b/drivers/reset/realtek/common.c
> new file mode 100644
> index 000000000000..ea7ff27117e7
> --- /dev/null
> +++ b/drivers/reset/realtek/common.c
> @@ -0,0 +1,85 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2019 Realtek Semiconductor Corporation
> + */
> +
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regmap.h>
> +#include "common.h"
> +
> +static inline struct rtk_reset_data *to_rtk_reset_controller(struct reset_controller_dev *r)
> +{
> + return container_of(r, struct rtk_reset_data, rcdev);
> +}
> +
> +static inline struct rtk_reset_desc *rtk_reset_get_desc(struct rtk_reset_data *data,
> + unsigned long idx)
> +{
> + return &data->descs[idx];
> +}
> +
> +static int rtk_reset_assert(struct reset_controller_dev *rcdev,
> + unsigned long idx)
> +{
> + struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
> + struct rtk_reset_desc *desc = rtk_reset_get_desc(data, idx);
> + u32 mask = desc->write_en ? (0x3 << desc->bit) : BIT(desc->bit);
> + u32 val = desc->write_en ? (0x2 << desc->bit) : 0;
> +
> + return regmap_update_bits(data->regmap, desc->ofs, mask, val);
> +}
> +
> +static int rtk_reset_deassert(struct reset_controller_dev *rcdev,
> + unsigned long idx)
> +{
> + struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
> + struct rtk_reset_desc *desc = rtk_reset_get_desc(data, idx);
> + u32 mask = desc->write_en ? (0x3 << desc->bit) : BIT(desc->bit);
> + u32 val = mask;
> +
> + return regmap_update_bits(data->regmap, desc->ofs, mask, val);
> +}
> +
> +static int rtk_reset_status(struct reset_controller_dev *rcdev,
> + unsigned long idx)
> +{
> + struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
> + struct rtk_reset_desc *desc = rtk_reset_get_desc(data, idx);
> + u32 val;
> + int ret;
> +
> + ret = regmap_read(data->regmap, desc->ofs, &val);
> + if (ret)
> + return ret;
> +
> + return !((val >> desc->bit) & 1);
> +}
> +
> +static const struct reset_control_ops rtk_reset_ops = {
> + .assert = rtk_reset_assert,
> + .deassert = rtk_reset_deassert,
> + .status = rtk_reset_status,
> +};
> +
> +/* The caller must initialize data->rcdev.nr_resets and data->descs before
> + * calling rtk_reset_controller_add().
> + */
> +int rtk_reset_controller_add(struct device *dev,
> + struct rtk_reset_data *data)
> +{
> + struct device *parent = dev->parent;
> +
> + data->regmap = dev_get_regmap(parent, NULL);
> + if (!data->regmap)
> + return -ENODEV;
> +
> + data->rcdev.owner = THIS_MODULE;
The rtk_reset_desc arrays used by this code live in the calling module,
so it would be better to let the caller initialize .owner as well.
It doesn't make a difference in practice, since CONFIG_RESET_RTK_COMMON
isn't tristate (right now).
regards
Philipp
^ permalink raw reply
* Re: [PATCH 2/4] perf arm_spe: Turn event name mappings into an array
From: Leo Yan @ 2026-04-02 9:13 UTC (permalink / raw)
To: James Clark
Cc: John Garry, Will Deacon, Mike Leach, Leo Yan, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
Al Grant, linux-arm-kernel, linux-perf-users, linux-kernel
In-Reply-To: <20260401-james-spe-impdef-decode-v1-2-ad0d372c220c@linaro.org>
On Wed, Apr 01, 2026 at 03:25:50PM +0100, James Clark wrote:
> This is so we can have a single function that prints events and can be
> used with multiple mappings from different CPUs. Remove any bit that was
> printed so that later we can print out the remaining unknown impdef
> bits.
>
> No functional changes intended.
>
> Signed-off-by: James Clark <james.clark@linaro.org>
> ---
> .../util/arm-spe-decoder/arm-spe-pkt-decoder.c | 88 +++++++++++-----------
> 1 file changed, 43 insertions(+), 45 deletions(-)
>
> diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
> index 5769ba2f4140..c880b0dec3a1 100644
> --- a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
> +++ b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
> @@ -276,6 +276,48 @@ static int arm_spe_pkt_out_string(int *err, char **buf_p, size_t *blen,
> return ret;
> }
>
> +struct ev_string {
> + u8 event;
> + const char *desc;
> +};
Maybe we can have more ambition to define a more general structure:
struct packet_field_string {
u64 bit_mask;
u64 bit_val;
const char *desc;
};
Thus, this also can be used by other bit fields decoding. We can do
this now or if later refactor for other packets.
Either way is fine for me:
Reviewed-by: Leo Yan <leo.yan@arm.com>
> +
> +static const struct ev_string common_ev_strings[] = {
> + { .event = EV_EXCEPTION_GEN, .desc = "EXCEPTION-GEN" },
> + { .event = EV_RETIRED, .desc = "RETIRED" },
> + { .event = EV_L1D_ACCESS, .desc = "L1D-ACCESS" },
> + { .event = EV_L1D_REFILL, .desc = "L1D-REFILL" },
> + { .event = EV_TLB_ACCESS, .desc = "TLB-ACCESS" },
> + { .event = EV_TLB_WALK, .desc = "TLB-REFILL" },
> + { .event = EV_NOT_TAKEN, .desc = "NOT-TAKEN" },
> + { .event = EV_MISPRED, .desc = "MISPRED" },
> + { .event = EV_LLC_ACCESS, .desc = "LLC-ACCESS" },
> + { .event = EV_LLC_MISS, .desc = "LLC-REFILL" },
> + { .event = EV_REMOTE_ACCESS, .desc = "REMOTE-ACCESS" },
> + { .event = EV_ALIGNMENT, .desc = "ALIGNMENT" },
> + { .event = EV_TRANSACTIONAL, .desc = "TXN" },
> + { .event = EV_PARTIAL_PREDICATE, .desc = "SVE-PARTIAL-PRED" },
> + { .event = EV_EMPTY_PREDICATE, .desc = "SVE-EMPTY-PRED" },
> + { .event = EV_L2D_ACCESS, .desc = "L2D-ACCESS" },
> + { .event = EV_L2D_MISS, .desc = "L2D-MISS" },
> + { .event = EV_CACHE_DATA_MODIFIED, .desc = "HITM" },
> + { .event = EV_RECENTLY_FETCHED, .desc = "LFB" },
> + { .event = EV_DATA_SNOOPED, .desc = "SNOOPED" },
> + { .event = EV_STREAMING_SVE_MODE, .desc = "STREAMING-SVE" },
> + { .event = EV_SMCU, .desc = "SMCU" },
> + { .event = 0, .desc = NULL },
> +};
> +
> +static u64 print_event_list(int *err, char **buf, size_t *buf_len,
> + const struct ev_string *ev_strings, u64 payload)
> +{
> + for (const struct ev_string *ev = ev_strings; ev->desc != NULL; ev++) {
> + if (payload & BIT(ev->event))
> + arm_spe_pkt_out_string(err, buf, buf_len, " %s", ev->desc);
> + payload &= ~BIT(ev->event);
> + }
> + return payload;
> +}
> +
> static int arm_spe_pkt_desc_event(const struct arm_spe_pkt *packet,
> char *buf, size_t buf_len)
> {
> @@ -283,51 +325,7 @@ static int arm_spe_pkt_desc_event(const struct arm_spe_pkt *packet,
> int err = 0;
>
> arm_spe_pkt_out_string(&err, &buf, &buf_len, "EV");
> -
> - if (payload & BIT(EV_EXCEPTION_GEN))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " EXCEPTION-GEN");
> - if (payload & BIT(EV_RETIRED))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " RETIRED");
> - if (payload & BIT(EV_L1D_ACCESS))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " L1D-ACCESS");
> - if (payload & BIT(EV_L1D_REFILL))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " L1D-REFILL");
> - if (payload & BIT(EV_TLB_ACCESS))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " TLB-ACCESS");
> - if (payload & BIT(EV_TLB_WALK))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " TLB-REFILL");
> - if (payload & BIT(EV_NOT_TAKEN))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " NOT-TAKEN");
> - if (payload & BIT(EV_MISPRED))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " MISPRED");
> - if (payload & BIT(EV_LLC_ACCESS))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " LLC-ACCESS");
> - if (payload & BIT(EV_LLC_MISS))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " LLC-REFILL");
> - if (payload & BIT(EV_REMOTE_ACCESS))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " REMOTE-ACCESS");
> - if (payload & BIT(EV_ALIGNMENT))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " ALIGNMENT");
> - if (payload & BIT(EV_TRANSACTIONAL))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " TXN");
> - if (payload & BIT(EV_PARTIAL_PREDICATE))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " SVE-PARTIAL-PRED");
> - if (payload & BIT(EV_EMPTY_PREDICATE))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " SVE-EMPTY-PRED");
> - if (payload & BIT(EV_L2D_ACCESS))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " L2D-ACCESS");
> - if (payload & BIT(EV_L2D_MISS))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " L2D-MISS");
> - if (payload & BIT(EV_CACHE_DATA_MODIFIED))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " HITM");
> - if (payload & BIT(EV_RECENTLY_FETCHED))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " LFB");
> - if (payload & BIT(EV_DATA_SNOOPED))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " SNOOPED");
> - if (payload & BIT(EV_STREAMING_SVE_MODE))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " STREAMING-SVE");
> - if (payload & BIT(EV_SMCU))
> - arm_spe_pkt_out_string(&err, &buf, &buf_len, " SMCU");
> + print_event_list(&err, &buf, &buf_len, common_ev_strings, payload);
>
> return err;
> }
>
> --
> 2.34.1
>
>
^ permalink raw reply
* RE: [EXTERNAL] Re: [PATCH v3 1/2] dt-bindings: perf: marvell: Document CN20K DDR PMU
From: Geethasowjanya Akula @ 2026-04-02 9:07 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
mark.rutland@arm.com, will@kernel.org, krzk+dt@kernel.org
In-Reply-To: <20260402-invaluable-delicate-clam-7fd6c5@quoll>
>-----Original Message-----
>From: Krzysztof Kozlowski <krzk@kernel.org>
>Sent: Thursday, April 2, 2026 12:47 PM
>To: Geethasowjanya Akula <gakula@marvell.com>
>Cc: linux-perf-users@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm-
>kernel@lists.infradead.org; devicetree@vger.kernel.org;
>mark.rutland@arm.com; will@kernel.org; krzk+dt@kernel.org
>Subject: [EXTERNAL] Re: [PATCH v3 1/2] dt-bindings: perf: marvell: Document
>CN20K DDR PMU
>
>On Wed, Apr 01, 2026 at 01:46:39PM +0530, Geetha sowjanya wrote:
>> Add a devicetree binding for the Marvell CN20K DDR performance monitor
>> block, including the marvell,cn20k-ddr-pmu compatible string and the
>> required MMIO reg region.
>
>You just repeated the diff. No need, we can read the diff, but what we cannot
>read is the hardware you are here describing.
>
Thank you for the review.
You are correct — the commit message and cover letter did not had sufficient
hardware context beyond what is already present in the diff.
CN20K is the successor to CN10K, and the DDR PMU hardware block is functionally
identical to the CN10K DDR PMU, with only minor register offset differences.
This series extends the existing CN10K ddr driver to support CN20K by accounting for those
offset changes.
I will update the commit description to clearly document the DDR PMU hardware,
its relationship to the CN10K implementation.
>>
>> Signed-off-by: Geetha sowjanya <gakula@marvell.com>
>> ---
>> .../bindings/perf/marvell-cn20k-ddr.yaml | 39 +++++++++++++++++++
>
>So you did not test v1. You did not test v2.
>
>Did you finally test this one before sending?
Yes, this version has been validated by running make dt_binding_check
and boot-tested on a CN20K simulator.
>
>> 1 file changed, 39 insertions(+)
>> create mode 100644
>> Documentation/devicetree/bindings/perf/marvell-cn20k-ddr.yaml
>>
>> diff --git
>> a/Documentation/devicetree/bindings/perf/marvell-cn20k-ddr.yaml
>> b/Documentation/devicetree/bindings/perf/marvell-cn20k-ddr.yaml
>> new file mode 100644
>> index 000000000000..fa757017d66e
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/perf/marvell-cn20k-ddr.yaml
>> @@ -0,0 +1,39 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2
>> +---
>> +$id:
>> +https://urldefense.proofpoint.com/v2/url?u=http-3A__devicetree.org_sc
>> +hemas_perf_marvell-2Dcn20k-2Dddr.yaml-
>23&d=DwIBaQ&c=nKjWec2b6R0mOyPaz
>>
>+7xtfQ&r=UiEt_nUeYFctu7JVLXVlXDhTmq_EAfooaZEYInfGuEQ&m=DMPRMbcsa
>CQDqOv
>> +Hx1f-Oqls-
>X7ZqhI8W9wFel75rh79c2Z0SJ936gMxT4qaMiA5&s=fLAYBOTd64cW4cp0e
>> +b-wxZI6vgY49R2E8M7EUoCk8y4&e=
>> +$schema:
>> +https://urldefense.proofpoint.com/v2/url?u=http-3A__devicetree.org_me
>> +ta-2Dschemas_core.yaml-
>23&d=DwIBaQ&c=nKjWec2b6R0mOyPaz7xtfQ&r=UiEt_nU
>> +eYFctu7JVLXVlXDhTmq_EAfooaZEYInfGuEQ&m=DMPRMbcsaCQDqOvHx1f-
>Oqls-X7Zqh
>> +I8W9wFel75rh79c2Z0SJ936gMxT4qaMiA5&s=VeGgq23L-
>AbFpFxV4e15wgUvn0yEbUDP
>> +xDzNa-1cJ94&e=
>> +
>> +title: Marvell CN20K DDR performance monitor
>> +
>> +description:
>> + Performance Monitoring Unit (PMU) for the DDR controller
>> + in Marvell CN20K SoCs.
>> +
>> +maintainers:
>> + - Geetha sowjanya <gakula@marvell.com>
>> +
>> +properties:
>> + compatible:
>> + const: marvell,cn20k-ddr-pmu
>
>There is no such thing as marvell,cn20k in upstream. What's that?
>
CN20K is the successor to CN10K . Will also re‑check CN20K naming
against existing upstream Marvell SoC compatibles.
>Best regards,
>Krzysztof
Thanks,
Geetha.
^ permalink raw reply
* Re: [PATCH 1/4] perf arm_spe: Make a function to get the MIDR
From: Leo Yan @ 2026-04-02 8:55 UTC (permalink / raw)
To: James Clark
Cc: John Garry, Will Deacon, Mike Leach, Leo Yan, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
Al Grant, linux-arm-kernel, linux-perf-users, linux-kernel
In-Reply-To: <20260401-james-spe-impdef-decode-v1-1-ad0d372c220c@linaro.org>
On Wed, Apr 01, 2026 at 03:25:49PM +0100, James Clark wrote:
> We'll need the MIDR to dump IMPDEF events in the next commits so extract
> a function for it.
>
> No functional changes intended.
>
> Signed-off-by: James Clark <james.clark@linaro.org>
Reviewed-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/arm-spe.c | 36 ++++++++++++++++++++++--------------
> 1 file changed, 22 insertions(+), 14 deletions(-)
>
> diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c
> index 70dd9bee47c7..7447b000f9cd 100644
> --- a/tools/perf/util/arm-spe.c
> +++ b/tools/perf/util/arm-spe.c
> @@ -958,14 +958,9 @@ static void arm_spe__synth_memory_level(struct arm_spe_queue *speq,
> }
> }
>
> -static void arm_spe__synth_ds(struct arm_spe_queue *speq,
> - const struct arm_spe_record *record,
> - union perf_mem_data_src *data_src)
> +static int arm_spe__get_midr(struct arm_spe *spe, int cpu, u64 *midr)
> {
> - struct arm_spe *spe = speq->spe;
> - u64 *metadata = NULL;
> - u64 midr;
> - unsigned int i;
> + u64 *metadata;
>
> /* Metadata version 1 assumes all CPUs are the same (old behavior) */
> if (spe->metadata_ver == 1) {
> @@ -973,15 +968,28 @@ static void arm_spe__synth_ds(struct arm_spe_queue *speq,
>
> pr_warning_once("Old SPE metadata, re-record to improve decode accuracy\n");
> cpuid = perf_env__cpuid(perf_session__env(spe->session));
> - midr = strtol(cpuid, NULL, 16);
> - } else {
> - metadata = arm_spe__get_metadata_by_cpu(spe, speq->cpu);
> - if (!metadata)
> - return;
> -
> - midr = metadata[ARM_SPE_CPU_MIDR];
> + *midr = strtol(cpuid, NULL, 16);
> + return 0;
> }
>
> + metadata = arm_spe__get_metadata_by_cpu(spe, cpu);
> + if (!metadata)
> + return -EINVAL;
> +
> + *midr = metadata[ARM_SPE_CPU_MIDR];
> + return 0;
> +}
> +
> +static void arm_spe__synth_ds(struct arm_spe_queue *speq,
> + const struct arm_spe_record *record,
> + union perf_mem_data_src *data_src)
> +{
> + u64 midr;
> + unsigned int i;
> +
> + if (arm_spe__get_midr(speq->spe, speq->cpu, &midr))
> + return;
> +
> for (i = 0; i < ARRAY_SIZE(data_source_handles); i++) {
> if (is_midr_in_range_list(midr, data_source_handles[i].midr_ranges)) {
> return data_source_handles[i].ds_synth(record, data_src);
>
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [PATCH v1 00/27] KVM: s390: Introduce arm64 KVM
From: David Hildenbrand (Arm) @ 2026-04-02 8:53 UTC (permalink / raw)
To: Steffen Eiden, kvm, kvmarm, linux-arm-kernel, linux-kernel,
linux-s390
Cc: Andreas Grapentin, Arnd Bergmann, Catalin Marinas,
Christian Borntraeger, Claudio Imbrenda, Gautam Gala,
Hendrik Brueckner, Janosch Frank, Joey Gouly, Marc Zyngier,
Nina Schoetterl-Glausch, Oliver Upton, Paolo Bonzini,
Suzuki K Poulose, Ulrich Weigand, Will Deacon, Zenghui Yu
In-Reply-To: <20260402042125.3948963-1-seiden@linux.ibm.com>
>
> KVM on s390:
> The SAE (Start Arm Execution) instruction is introduced as the
> s390 mechanism for running Arm64 guests, and a new kvm-arm64 module is
> built up incrementally.
>
> Upcoming patch series will introduce system-register handling, interrupt
> support, hypercalls, and additional features such as PMU.
Pretty cool stuff.
What's the rough timeline for the other work?
Regarding I/O, I guess it is primarily VIRTIO (VIRTIO_PCI) for these VMs
only?
--
Cheers,
David
^ permalink raw reply
* Re: [PATCH 0/5] crc64: Tweak intrinsics code and enable it for ARM
From: Ard Biesheuvel @ 2026-04-02 8:52 UTC (permalink / raw)
To: Eric Biggers; +Cc: linux-crypto, linux-arm-kernel, Demian Shulhan
In-Reply-To: <20260401195943.GA2466@quark>
On Wed, 1 Apr 2026, at 21:59, Eric Biggers wrote:
> On Mon, Mar 30, 2026 at 04:46:31PM +0200, Ard Biesheuvel wrote:
>> Apply some tweaks to the new arm64 crc64 NEON intrinsics code, and wire
>> it up for the 32-bit ARM build. Note that true 32-bit ARM CPUs usually
>> don't implement the prerequisite 64x64 PMULL instructions, but 32-bit
>> kernels are commonly used on 64-bit capable hardware too, which do
>> implement the 32-bit versions of the crypto instructions if they are
>> implemented for the 64-bit ISA (as per the architecture).
>>
>> Cc: Demian Shulhan <demyansh@gmail.com>
>> Cc: Eric Biggers <ebiggers@kernel.org>
>>
>> Ard Biesheuvel (5):
>> lib/crc: arm64: Drop unnecessary chunking logic from crc64
>> lib/crc: arm64: Use existing macros for kernel-mode FPU cflags
>> ARM: Add a neon-intrinsics.h header like on arm64
>> lib/crc: arm64: Simplify intrinsics implementation
>> lib/crc: arm: Enable arm64's NEON intrinsics implementation of crc64
>
> I think patches 3 and 4 should be swapped, so it's cleanups first (which
> make sense regardless of the 32-bit ARM support) and then the 32-bit ARM
> support.
>
Ok.
> I do think we should be aware that even with the code mostly shared
> using the NEON intrinsics, the 32-bit ARM support (which works only on
> CPUs that support PMULL, i.e. are also 64-bit capable) doesn't come for
> free. We should expect to deal with occasional issues related to the
> intrinsics with certain compiler versions, compiler flags, etc.
>
> I assume that "32-bit kernels on ARMv8 CPUs" is currently still a big
> enough niche to bother with this, despite that niche getting smaller
> over time.
Running a 32-bit kernel on 64-bit capable hardware is usually done to reduce the RAM footprint, and that problem hasn't gotten any smaller lately. And 20x speedup is rather significant.
> But as I mentioned I do think we should try to simplify it
> as much as possible, e.g. by supporting little-endian only and avoiding
> #ifdefs based on things like the compiler whenever possible.
>
Sure. The only reason I think this is worth the effort is because the same code can be used on ARM and arm64, so once this is no longer the case, I don't think we should bother.
So it makes sense to apply this reasoning to little endian as well - arm64 supports it so we can support in on ARM too.
^ 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