* [PATCH RESEND v4 1/8] can: flexcan: use dedicated IRQ handlers for multi-IRQ platforms
From: Ciprian Costea @ 2026-04-21 10:25 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
Fabio Estevam
Cc: Pengutronix Kernel Team, linux-can, devicetree, linux-kernel, imx,
linux-arm-kernel, NXP S32 Linux Team, Christophe Lizzi,
Alberto Ruiz, Enric Balletbo, Eric Chanudet,
Ciprian Marian Costea
In-Reply-To: <20260421102603.4122332-1-ciprianmarian.costea@oss.nxp.com>
From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
On platforms with multiple IRQ lines (S32G2, MCF5441X), all lines are
registered to the same flexcan_irq() handler. Since these are distinct IRQ
numbers, they can be dispatched concurrently on different CPUs. Both
instances then read the same iflag and ESR registers unconditionally,
leading to duplicate frame processing.
Fix this by splitting the monolithic handler into focused parts:
- flexcan_do_mb(): processes mailbox events
- flexcan_do_state(): processes device state change events
- flexcan_do_berr(): processes bus error events
Introduce dedicated IRQ handlers for multi-IRQ platforms:
- flexcan_irq_mb(): mailbox-only, used for mb-0, mb-1 IRQ lines
- flexcan_irq_boff(): state-change-only, used for boff/state IRQ line
- flexcan_irq_berr(): bus-error-only, used for berr IRQ line
The combined flexcan_irq() handler is preserved for single-IRQ
platforms with no functional change.
Fixes: d9cead75b1c6 ("can: flexcan: add mcf5441x support")
Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
---
drivers/net/can/flexcan/flexcan-core.c | 128 +++++++++++++++++++++----
1 file changed, 111 insertions(+), 17 deletions(-)
diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c
index f5d22c61503f..f73ff442d530 100644
--- a/drivers/net/can/flexcan/flexcan-core.c
+++ b/drivers/net/can/flexcan/flexcan-core.c
@@ -1070,16 +1070,14 @@ static struct sk_buff *flexcan_mailbox_read(struct can_rx_offload *offload,
return skb;
}
-static irqreturn_t flexcan_irq(int irq, void *dev_id)
+/* Process mailbox (RX + TX) events */
+static irqreturn_t flexcan_do_mb(struct net_device *dev)
{
- struct net_device *dev = dev_id;
struct net_device_stats *stats = &dev->stats;
struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->regs;
irqreturn_t handled = IRQ_NONE;
u64 reg_iflag_tx;
- u32 reg_esr;
- enum can_state last_state = priv->can.state;
/* reception interrupt */
if (priv->devtype_data.quirks & FLEXCAN_QUIRK_USE_RX_MAILBOX) {
@@ -1131,25 +1129,57 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
netif_wake_queue(dev);
}
+ return handled;
+}
+
+/* Process bus error events */
+static irqreturn_t flexcan_do_berr(struct net_device *dev)
+{
+ struct flexcan_priv *priv = netdev_priv(dev);
+ struct flexcan_regs __iomem *regs = priv->regs;
+ irqreturn_t handled = IRQ_NONE;
+ u32 reg_esr;
+
reg_esr = priv->read(®s->esr);
- /* ACK all bus error, state change and wake IRQ sources */
- if (reg_esr & (FLEXCAN_ESR_ALL_INT | FLEXCAN_ESR_WAK_INT)) {
+ /* ACK bus error interrupt source */
+ if (reg_esr & FLEXCAN_ESR_ERR_INT) {
handled = IRQ_HANDLED;
- priv->write(reg_esr & (FLEXCAN_ESR_ALL_INT | FLEXCAN_ESR_WAK_INT), ®s->esr);
+ priv->write(FLEXCAN_ESR_ERR_INT, ®s->esr);
}
- /* state change interrupt or broken error state quirk fix is enabled */
- if ((reg_esr & FLEXCAN_ESR_ERR_STATE) ||
- (priv->devtype_data.quirks & (FLEXCAN_QUIRK_BROKEN_WERR_STATE |
- FLEXCAN_QUIRK_BROKEN_PERR_STATE)))
- flexcan_irq_state(dev, reg_esr);
-
/* bus error IRQ - handle if bus error reporting is activated */
if ((reg_esr & FLEXCAN_ESR_ERR_BUS) &&
(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING))
flexcan_irq_bus_err(dev, reg_esr);
+ return handled;
+}
+
+/* Process device state change events */
+static irqreturn_t flexcan_do_state(struct net_device *dev)
+{
+ struct flexcan_priv *priv = netdev_priv(dev);
+ struct flexcan_regs __iomem *regs = priv->regs;
+ irqreturn_t handled = IRQ_NONE;
+ u32 reg_esr;
+ enum can_state last_state = priv->can.state;
+
+ reg_esr = priv->read(®s->esr);
+
+ /* ACK state change and wake IRQ sources */
+ if (reg_esr & (FLEXCAN_ESR_ERR_STATE | FLEXCAN_ESR_WAK_INT)) {
+ handled = IRQ_HANDLED;
+ priv->write(reg_esr & (FLEXCAN_ESR_ERR_STATE | FLEXCAN_ESR_WAK_INT),
+ ®s->esr);
+ }
+
+ /* state change interrupt or broken error state quirk fix is enabled */
+ if ((reg_esr & FLEXCAN_ESR_ERR_STATE) ||
+ (priv->devtype_data.quirks &
+ (FLEXCAN_QUIRK_BROKEN_WERR_STATE | FLEXCAN_QUIRK_BROKEN_PERR_STATE)))
+ flexcan_irq_state(dev, reg_esr);
+
/* availability of error interrupt among state transitions in case
* bus error reporting is de-activated and
* FLEXCAN_QUIRK_BROKEN_PERR_STATE is enabled:
@@ -1188,6 +1218,65 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
}
}
+ return handled;
+}
+
+/* Combined IRQ handler for single-IRQ platforms */
+static irqreturn_t flexcan_irq(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct flexcan_priv *priv = netdev_priv(dev);
+ irqreturn_t handled;
+
+ handled = flexcan_do_mb(dev);
+ handled |= flexcan_do_state(dev);
+ handled |= flexcan_do_berr(dev);
+
+ if (handled)
+ can_rx_offload_irq_finish(&priv->offload);
+
+ return handled;
+}
+
+/* Mailbox IRQ handler for multi-IRQ platforms */
+static irqreturn_t flexcan_irq_mb(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct flexcan_priv *priv = netdev_priv(dev);
+ irqreturn_t handled;
+
+ handled = flexcan_do_mb(dev);
+
+ if (handled)
+ can_rx_offload_irq_finish(&priv->offload);
+
+ return handled;
+}
+
+/* Bus error IRQ handler for multi-IRQ platforms */
+static irqreturn_t flexcan_irq_berr(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct flexcan_priv *priv = netdev_priv(dev);
+ irqreturn_t handled;
+
+ handled = flexcan_do_berr(dev);
+
+ if (handled)
+ can_rx_offload_irq_finish(&priv->offload);
+
+ return handled;
+}
+
+/* Device state change IRQ handler for multi-IRQ platforms */
+static irqreturn_t flexcan_irq_boff(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct flexcan_priv *priv = netdev_priv(dev);
+ irqreturn_t handled;
+
+ handled = flexcan_do_state(dev);
+
if (handled)
can_rx_offload_irq_finish(&priv->offload);
@@ -1761,25 +1850,30 @@ static int flexcan_open(struct net_device *dev)
can_rx_offload_enable(&priv->offload);
- err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev);
+ if (priv->devtype_data.quirks & FLEXCAN_QUIRK_NR_IRQ_3)
+ err = request_irq(dev->irq, flexcan_irq_mb,
+ IRQF_SHARED, dev->name, dev);
+ else
+ err = request_irq(dev->irq, flexcan_irq,
+ IRQF_SHARED, dev->name, dev);
if (err)
goto out_can_rx_offload_disable;
if (priv->devtype_data.quirks & FLEXCAN_QUIRK_NR_IRQ_3) {
err = request_irq(priv->irq_boff,
- flexcan_irq, IRQF_SHARED, dev->name, dev);
+ flexcan_irq_boff, IRQF_SHARED, dev->name, dev);
if (err)
goto out_free_irq;
err = request_irq(priv->irq_err,
- flexcan_irq, IRQF_SHARED, dev->name, dev);
+ flexcan_irq_berr, IRQF_SHARED, dev->name, dev);
if (err)
goto out_free_irq_boff;
}
if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SECONDARY_MB_IRQ) {
err = request_irq(priv->irq_secondary_mb,
- flexcan_irq, IRQF_SHARED, dev->name, dev);
+ flexcan_irq_mb, IRQF_SHARED, dev->name, dev);
if (err)
goto out_free_irq_err;
}
--
2.43.0
^ permalink raw reply related
* [PATCH RESEND v4 2/8] can: flexcan: disable all IRQ lines in flexcan_chip_interrupts_enable()
From: Ciprian Costea @ 2026-04-21 10:25 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
Fabio Estevam
Cc: Pengutronix Kernel Team, linux-can, devicetree, linux-kernel, imx,
linux-arm-kernel, NXP S32 Linux Team, Christophe Lizzi,
Alberto Ruiz, Enric Balletbo, Eric Chanudet,
Ciprian Marian Costea
In-Reply-To: <20260421102603.4122332-1-ciprianmarian.costea@oss.nxp.com>
From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
flexcan_chip_interrupts_enable() disables only the primary IRQ line while
writing to the IMASK and CTRL registers.
On multi-IRQ platforms (S32G2, MCF5441X), the additional IRQ lines (boff,
err, secondary-mb) remain active so their handlers can fire while
registers are inconsistent.
Disable all registered IRQ lines around the IMASK/CTRL writes. This
also fixes the resume path, which calls this function.
Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
---
drivers/net/can/flexcan/flexcan-core.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c
index f73ff442d530..7dde2e623def 100644
--- a/drivers/net/can/flexcan/flexcan-core.c
+++ b/drivers/net/can/flexcan/flexcan-core.c
@@ -1519,14 +1519,28 @@ static void flexcan_chip_interrupts_enable(const struct net_device *dev)
{
const struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->regs;
+ u32 quirks = priv->devtype_data.quirks;
u64 reg_imask;
disable_irq(dev->irq);
+ if (quirks & FLEXCAN_QUIRK_NR_IRQ_3) {
+ disable_irq(priv->irq_boff);
+ disable_irq(priv->irq_err);
+ }
+ if (quirks & FLEXCAN_QUIRK_SECONDARY_MB_IRQ)
+ disable_irq(priv->irq_secondary_mb);
+
priv->write(priv->reg_ctrl_default, ®s->ctrl);
reg_imask = priv->rx_mask | priv->tx_mask;
priv->write(upper_32_bits(reg_imask), ®s->imask2);
priv->write(lower_32_bits(reg_imask), ®s->imask1);
enable_irq(dev->irq);
+ if (quirks & FLEXCAN_QUIRK_SECONDARY_MB_IRQ)
+ enable_irq(priv->irq_secondary_mb);
+ if (quirks & FLEXCAN_QUIRK_NR_IRQ_3) {
+ enable_irq(priv->irq_boff);
+ enable_irq(priv->irq_err);
+ }
}
static void flexcan_chip_interrupts_disable(const struct net_device *dev)
--
2.43.0
^ permalink raw reply related
* [PATCH RESEND v4 4/8] dt-bindings: can: fsl,flexcan: add NXP S32N79 SoC support
From: Ciprian Costea @ 2026-04-21 10:25 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
Fabio Estevam
Cc: Pengutronix Kernel Team, linux-can, devicetree, linux-kernel, imx,
linux-arm-kernel, NXP S32 Linux Team, Christophe Lizzi,
Alberto Ruiz, Enric Balletbo, Eric Chanudet,
Ciprian Marian Costea, Andra-Teodora Ilie, Larisa Grigore,
Conor Dooley
In-Reply-To: <20260421102603.4122332-1-ciprianmarian.costea@oss.nxp.com>
From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
Add NXP S32N79 SoC compatible string and interrupt properties.
On S32N79, FlexCAN IP is integrated with two interrupt lines:
one for the mailbox interrupts (0-127) and one for signaling
bus errors and device state changes.
Co-developed-by: Andra-Teodora Ilie <andra.ilie@nxp.com>
Signed-off-by: Andra-Teodora Ilie <andra.ilie@nxp.com>
Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com>
Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com>
Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
.../bindings/net/can/fsl,flexcan.yaml | 30 ++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml b/Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml
index f81d56f7c12a..d098a44c2b9c 100644
--- a/Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml
+++ b/Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml
@@ -26,6 +26,7 @@ properties:
- fsl,ls1021ar2-flexcan
- fsl,lx2160ar1-flexcan
- nxp,s32g2-flexcan
+ - nxp,s32n79-flexcan
- items:
- enum:
- fsl,imx53-flexcan
@@ -173,11 +174,38 @@ allOf:
- const: mb-1
required:
- interrupt-names
- else:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: nxp,s32n79-flexcan
+ then:
+ properties:
+ interrupts:
+ items:
+ - description: Message Buffer interrupt for mailboxes 0-127
+ - description: Bus Error and Device state change interrupt
+ interrupt-names:
+ items:
+ - const: mb-0
+ - const: berr
+ required:
+ - interrupt-names
+
+ - if:
+ not:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - nxp,s32g2-flexcan
+ - nxp,s32n79-flexcan
+ then:
properties:
interrupts:
maxItems: 1
interrupt-names: false
+
- if:
required:
- xceiver-supply
--
2.43.0
^ permalink raw reply related
* [PATCH RESEND v4 5/8] can: flexcan: add FLEXCAN_QUIRK_IRQ_BERR quirk
From: Ciprian Costea @ 2026-04-21 10:26 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
Fabio Estevam
Cc: Pengutronix Kernel Team, linux-can, devicetree, linux-kernel, imx,
linux-arm-kernel, NXP S32 Linux Team, Christophe Lizzi,
Alberto Ruiz, Enric Balletbo, Eric Chanudet,
Ciprian Marian Costea, Larisa Grigore
In-Reply-To: <20260421102603.4122332-1-ciprianmarian.costea@oss.nxp.com>
From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
Introduce FLEXCAN_QUIRK_IRQ_BERR quirk to handle hardware integration
where the FlexCAN module has a dedicated interrupt line for signaling
bus errors and device state changes.
This adds the flexcan_irq_esr() handler which composes
flexcan_do_state() and flexcan_do_berr() to handle platforms where
these events share a single IRQ line.
Also extend flexcan_chip_interrupts_enable() to disable/enable the
new IRQ line during IMASK register writes.
This is required for NXP S32N79 SoC support.
Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com>
Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com>
Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
---
drivers/net/can/flexcan/flexcan-core.c | 54 +++++++++++++++++++++-----
drivers/net/can/flexcan/flexcan.h | 2 +
2 files changed, 47 insertions(+), 9 deletions(-)
diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c
index 32e4d4da00a1..23ddf7910641 100644
--- a/drivers/net/can/flexcan/flexcan-core.c
+++ b/drivers/net/can/flexcan/flexcan-core.c
@@ -1293,6 +1293,22 @@ static irqreturn_t flexcan_irq_boff(int irq, void *dev_id)
return handled;
}
+/* Combined bus error and state change IRQ handler */
+static irqreturn_t flexcan_irq_esr(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct flexcan_priv *priv = netdev_priv(dev);
+ irqreturn_t handled;
+
+ handled = flexcan_do_state(dev);
+ handled |= flexcan_do_berr(dev);
+
+ if (handled)
+ can_rx_offload_irq_finish(&priv->offload);
+
+ return handled;
+}
+
static void flexcan_set_bittiming_ctrl(const struct net_device *dev)
{
const struct flexcan_priv *priv = netdev_priv(dev);
@@ -1549,10 +1565,10 @@ static void flexcan_chip_interrupts_enable(const struct net_device *dev)
u64 reg_imask;
disable_irq(dev->irq);
- if (quirks & FLEXCAN_QUIRK_NR_IRQ_3) {
+ if (quirks & FLEXCAN_QUIRK_NR_IRQ_3)
disable_irq(priv->irq_boff);
+ if (quirks & (FLEXCAN_QUIRK_NR_IRQ_3 | FLEXCAN_QUIRK_IRQ_BERR))
disable_irq(priv->irq_err);
- }
if (quirks & FLEXCAN_QUIRK_SECONDARY_MB_IRQ)
disable_irq(priv->irq_secondary_mb);
@@ -1564,10 +1580,10 @@ static void flexcan_chip_interrupts_enable(const struct net_device *dev)
enable_irq(dev->irq);
if (quirks & FLEXCAN_QUIRK_SECONDARY_MB_IRQ)
enable_irq(priv->irq_secondary_mb);
- if (quirks & FLEXCAN_QUIRK_NR_IRQ_3) {
- enable_irq(priv->irq_boff);
+ if (quirks & (FLEXCAN_QUIRK_NR_IRQ_3 | FLEXCAN_QUIRK_IRQ_BERR))
enable_irq(priv->irq_err);
- }
+ if (quirks & FLEXCAN_QUIRK_NR_IRQ_3)
+ enable_irq(priv->irq_boff);
}
static void flexcan_chip_interrupts_disable(const struct net_device *dev)
@@ -1891,7 +1907,8 @@ static int flexcan_open(struct net_device *dev)
can_rx_offload_enable(&priv->offload);
- if (priv->devtype_data.quirks & FLEXCAN_QUIRK_NR_IRQ_3)
+ if (priv->devtype_data.quirks &
+ (FLEXCAN_QUIRK_NR_IRQ_3 | FLEXCAN_QUIRK_IRQ_BERR))
err = request_irq(dev->irq, flexcan_irq_mb,
IRQF_SHARED, dev->name, dev);
else
@@ -1912,6 +1929,13 @@ static int flexcan_open(struct net_device *dev)
goto out_free_irq_boff;
}
+ if (priv->devtype_data.quirks & FLEXCAN_QUIRK_IRQ_BERR) {
+ err = request_irq(priv->irq_err,
+ flexcan_irq_esr, IRQF_SHARED, dev->name, dev);
+ if (err)
+ goto out_free_irq_boff;
+ }
+
if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SECONDARY_MB_IRQ) {
err = request_irq(priv->irq_secondary_mb,
flexcan_irq_mb, IRQF_SHARED, dev->name, dev);
@@ -1926,7 +1950,8 @@ static int flexcan_open(struct net_device *dev)
return 0;
out_free_irq_err:
- if (priv->devtype_data.quirks & FLEXCAN_QUIRK_NR_IRQ_3)
+ if (priv->devtype_data.quirks &
+ (FLEXCAN_QUIRK_IRQ_BERR | FLEXCAN_QUIRK_NR_IRQ_3))
free_irq(priv->irq_err, dev);
out_free_irq_boff:
if (priv->devtype_data.quirks & FLEXCAN_QUIRK_NR_IRQ_3)
@@ -1958,10 +1983,12 @@ static int flexcan_close(struct net_device *dev)
if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SECONDARY_MB_IRQ)
free_irq(priv->irq_secondary_mb, dev);
- if (priv->devtype_data.quirks & FLEXCAN_QUIRK_NR_IRQ_3) {
+ if (priv->devtype_data.quirks &
+ (FLEXCAN_QUIRK_IRQ_BERR | FLEXCAN_QUIRK_NR_IRQ_3))
free_irq(priv->irq_err, dev);
+
+ if (priv->devtype_data.quirks & FLEXCAN_QUIRK_NR_IRQ_3)
free_irq(priv->irq_boff, dev);
- }
free_irq(dev->irq, dev);
can_rx_offload_disable(&priv->offload);
@@ -2348,12 +2375,21 @@ static int flexcan_probe(struct platform_device *pdev)
if (transceiver)
priv->can.bitrate_max = transceiver->attrs.max_link_rate;
+ if (priv->devtype_data.quirks & FLEXCAN_QUIRK_IRQ_BERR) {
+ priv->irq_err = platform_get_irq_byname(pdev, "berr");
+ if (priv->irq_err < 0) {
+ err = priv->irq_err;
+ goto failed_platform_get_irq;
+ }
+ }
+
if (priv->devtype_data.quirks & FLEXCAN_QUIRK_NR_IRQ_3) {
priv->irq_boff = platform_get_irq(pdev, 1);
if (priv->irq_boff < 0) {
err = priv->irq_boff;
goto failed_platform_get_irq;
}
+
priv->irq_err = platform_get_irq(pdev, 2);
if (priv->irq_err < 0) {
err = priv->irq_err;
diff --git a/drivers/net/can/flexcan/flexcan.h b/drivers/net/can/flexcan/flexcan.h
index 22aa097ec3c0..43d4e0da3779 100644
--- a/drivers/net/can/flexcan/flexcan.h
+++ b/drivers/net/can/flexcan/flexcan.h
@@ -74,6 +74,8 @@
* both need to have an interrupt handler registered.
*/
#define FLEXCAN_QUIRK_SECONDARY_MB_IRQ BIT(18)
+/* Setup dedicated bus error and state change IRQ */
+#define FLEXCAN_QUIRK_IRQ_BERR BIT(19)
#define FLEXCAN_NR_MB_IRQS 2
--
2.43.0
^ permalink raw reply related
* [PATCH RESEND v4 3/8] can: flexcan: split rx/tx masks per mailbox IRQ line
From: Ciprian Costea @ 2026-04-21 10:25 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
Fabio Estevam
Cc: Pengutronix Kernel Team, linux-can, devicetree, linux-kernel, imx,
linux-arm-kernel, NXP S32 Linux Team, Christophe Lizzi,
Alberto Ruiz, Enric Balletbo, Eric Chanudet,
Ciprian Marian Costea
In-Reply-To: <20260421102603.4122332-1-ciprianmarian.costea@oss.nxp.com>
From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
On S32G2, which has two mailbox IRQ lines (mb-0 for MBs 0-7, mb-1
for MBs 8-127), both handlers currently process the full rx_mask/tx_mask
range,
Introduce struct flexcan_mb_irq to hold per-IRQ-line rx and tx masks.
In flexcan_irq_mb(), the irq argument selects the correct mask set: the
primary MB IRQ uses mb_irq[0] and the secondary uses mb_irq[1].
For single-IRQ platforms, mb_irq[0] holds the full combined masks with no
functional change.
Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
---
drivers/net/can/flexcan/flexcan-core.c | 61 +++++++++++++++++++-------
drivers/net/can/flexcan/flexcan.h | 10 ++++-
2 files changed, 52 insertions(+), 19 deletions(-)
diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c
index 7dde2e623def..32e4d4da00a1 100644
--- a/drivers/net/can/flexcan/flexcan-core.c
+++ b/drivers/net/can/flexcan/flexcan-core.c
@@ -957,14 +957,16 @@ static inline void flexcan_write64(struct flexcan_priv *priv, u64 val, void __io
priv->write(lower_32_bits(val), addr);
}
-static inline u64 flexcan_read_reg_iflag_rx(struct flexcan_priv *priv)
+static inline u64 flexcan_read_reg_iflag_rx(struct flexcan_priv *priv,
+ u64 rx_mask)
{
- return flexcan_read64_mask(priv, &priv->regs->iflag1, priv->rx_mask);
+ return flexcan_read64_mask(priv, &priv->regs->iflag1, rx_mask);
}
-static inline u64 flexcan_read_reg_iflag_tx(struct flexcan_priv *priv)
+static inline u64 flexcan_read_reg_iflag_tx(struct flexcan_priv *priv,
+ u64 tx_mask)
{
- return flexcan_read64_mask(priv, &priv->regs->iflag1, priv->tx_mask);
+ return flexcan_read64_mask(priv, &priv->regs->iflag1, tx_mask);
}
static inline struct flexcan_priv *rx_offload_to_priv(struct can_rx_offload *offload)
@@ -1071,7 +1073,8 @@ static struct sk_buff *flexcan_mailbox_read(struct can_rx_offload *offload,
}
/* Process mailbox (RX + TX) events */
-static irqreturn_t flexcan_do_mb(struct net_device *dev)
+static irqreturn_t flexcan_do_mb(struct net_device *dev,
+ const struct flexcan_mb_irq *mb_irq)
{
struct net_device_stats *stats = &dev->stats;
struct flexcan_priv *priv = netdev_priv(dev);
@@ -1084,7 +1087,8 @@ static irqreturn_t flexcan_do_mb(struct net_device *dev)
u64 reg_iflag_rx;
int ret;
- while ((reg_iflag_rx = flexcan_read_reg_iflag_rx(priv))) {
+ while ((reg_iflag_rx = flexcan_read_reg_iflag_rx(priv,
+ mb_irq->rx_mask))) {
handled = IRQ_HANDLED;
ret = can_rx_offload_irq_offload_timestamp(&priv->offload,
reg_iflag_rx);
@@ -1110,10 +1114,10 @@ static irqreturn_t flexcan_do_mb(struct net_device *dev)
}
}
- reg_iflag_tx = flexcan_read_reg_iflag_tx(priv);
+ reg_iflag_tx = flexcan_read_reg_iflag_tx(priv, mb_irq->tx_mask);
/* transmission complete interrupt */
- if (reg_iflag_tx & priv->tx_mask) {
+ if (reg_iflag_tx & mb_irq->tx_mask) {
u32 reg_ctrl = priv->read(&priv->tx_mb->can_ctrl);
handled = IRQ_HANDLED;
@@ -1125,7 +1129,7 @@ static irqreturn_t flexcan_do_mb(struct net_device *dev)
/* after sending a RTR frame MB is in RX mode */
priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
&priv->tx_mb->can_ctrl);
- flexcan_write64(priv, priv->tx_mask, ®s->iflag1);
+ flexcan_write64(priv, mb_irq->tx_mask, ®s->iflag1);
netif_wake_queue(dev);
}
@@ -1228,7 +1232,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
struct flexcan_priv *priv = netdev_priv(dev);
irqreturn_t handled;
- handled = flexcan_do_mb(dev);
+ handled = flexcan_do_mb(dev, &priv->mb_irq[0]);
handled |= flexcan_do_state(dev);
handled |= flexcan_do_berr(dev);
@@ -1243,9 +1247,15 @@ static irqreturn_t flexcan_irq_mb(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct flexcan_priv *priv = netdev_priv(dev);
+ const struct flexcan_mb_irq *mb_irq;
irqreturn_t handled;
+ int idx;
- handled = flexcan_do_mb(dev);
+ idx = (priv->devtype_data.quirks & FLEXCAN_QUIRK_SECONDARY_MB_IRQ &&
+ irq == priv->irq_secondary_mb) ? 1 : 0;
+ mb_irq = &priv->mb_irq[idx];
+
+ handled = flexcan_do_mb(dev, mb_irq);
if (handled)
can_rx_offload_irq_finish(&priv->offload);
@@ -1473,6 +1483,7 @@ static void flexcan_ram_init(struct net_device *dev)
static int flexcan_rx_offload_setup(struct net_device *dev)
{
struct flexcan_priv *priv = netdev_priv(dev);
+ u64 rx_mask, tx_mask;
int err;
if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
@@ -1494,20 +1505,35 @@ static int flexcan_rx_offload_setup(struct net_device *dev)
flexcan_get_mb(priv, FLEXCAN_TX_MB_RESERVED_RX_FIFO);
priv->tx_mb_idx = priv->mb_count - 1;
priv->tx_mb = flexcan_get_mb(priv, priv->tx_mb_idx);
- priv->tx_mask = FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
-
priv->offload.mailbox_read = flexcan_mailbox_read;
if (priv->devtype_data.quirks & FLEXCAN_QUIRK_USE_RX_MAILBOX) {
priv->offload.mb_first = FLEXCAN_RX_MB_RX_MAILBOX_FIRST;
priv->offload.mb_last = priv->mb_count - 2;
- priv->rx_mask = GENMASK_ULL(priv->offload.mb_last,
- priv->offload.mb_first);
+ rx_mask = GENMASK_ULL(priv->offload.mb_last,
+ priv->offload.mb_first);
+ tx_mask = FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
+
+ if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SECONDARY_MB_IRQ) {
+ /* S32G2 has two MB IRQ lines with the split at MB 8:
+ * mb-0 IRQ handles MBs 0-7,
+ * mb-1 IRQ handles MBs 8-127.
+ */
+ priv->mb_irq[0].rx_mask = rx_mask & GENMASK_ULL(7, 0);
+ priv->mb_irq[0].tx_mask = tx_mask & GENMASK_ULL(7, 0);
+ priv->mb_irq[1].rx_mask = rx_mask & GENMASK_ULL(63, 8);
+ priv->mb_irq[1].tx_mask = tx_mask & GENMASK_ULL(63, 8);
+ } else {
+ priv->mb_irq[0].rx_mask = rx_mask;
+ priv->mb_irq[0].tx_mask = tx_mask;
+ }
+
err = can_rx_offload_add_timestamp(dev, &priv->offload);
} else {
- priv->rx_mask = FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
+ priv->mb_irq[0].rx_mask = FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
FLEXCAN_IFLAG_RX_FIFO_AVAILABLE;
+ priv->mb_irq[0].tx_mask = FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
err = can_rx_offload_add_fifo(dev, &priv->offload,
FLEXCAN_NAPI_WEIGHT);
}
@@ -1531,7 +1557,8 @@ static void flexcan_chip_interrupts_enable(const struct net_device *dev)
disable_irq(priv->irq_secondary_mb);
priv->write(priv->reg_ctrl_default, ®s->ctrl);
- reg_imask = priv->rx_mask | priv->tx_mask;
+ reg_imask = priv->mb_irq[0].rx_mask | priv->mb_irq[0].tx_mask |
+ priv->mb_irq[1].rx_mask | priv->mb_irq[1].tx_mask;
priv->write(upper_32_bits(reg_imask), ®s->imask2);
priv->write(lower_32_bits(reg_imask), ®s->imask1);
enable_irq(dev->irq);
diff --git a/drivers/net/can/flexcan/flexcan.h b/drivers/net/can/flexcan/flexcan.h
index 16692a2502eb..22aa097ec3c0 100644
--- a/drivers/net/can/flexcan/flexcan.h
+++ b/drivers/net/can/flexcan/flexcan.h
@@ -75,10 +75,17 @@
*/
#define FLEXCAN_QUIRK_SECONDARY_MB_IRQ BIT(18)
+#define FLEXCAN_NR_MB_IRQS 2
+
struct flexcan_devtype_data {
u32 quirks; /* quirks needed for different IP cores */
};
+struct flexcan_mb_irq {
+ u64 rx_mask;
+ u64 tx_mask;
+};
+
struct flexcan_stop_mode {
struct regmap *gpr;
u8 req_gpr;
@@ -99,8 +106,7 @@ struct flexcan_priv {
u8 clk_src; /* clock source of CAN Protocol Engine */
u8 scu_idx;
- u64 rx_mask;
- u64 tx_mask;
+ struct flexcan_mb_irq mb_irq[FLEXCAN_NR_MB_IRQS];
u32 reg_ctrl_default;
struct clk *clk_ipg;
--
2.43.0
^ permalink raw reply related
* [PATCH RESEND v4 6/8] can: flexcan: add NXP S32N79 SoC support
From: Ciprian Costea @ 2026-04-21 10:26 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
Fabio Estevam
Cc: Pengutronix Kernel Team, linux-can, devicetree, linux-kernel, imx,
linux-arm-kernel, NXP S32 Linux Team, Christophe Lizzi,
Alberto Ruiz, Enric Balletbo, Eric Chanudet,
Ciprian Marian Costea, Andra-Teodora Ilie, Larisa Grigore
In-Reply-To: <20260421102603.4122332-1-ciprianmarian.costea@oss.nxp.com>
From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
Add device data and compatible string for NXP S32N79 SoC.
FlexCAN IP integration on S32N79 SoC uses two interrupts:
- one for mailboxes 0-127
- one for signaling bus errors and device state changes
Co-developed-by: Andra-Teodora Ilie <andra.ilie@nxp.com>
Signed-off-by: Andra-Teodora Ilie <andra.ilie@nxp.com>
Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com>
Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com>
Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
---
drivers/net/can/flexcan/flexcan-core.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c
index 23ddf7910641..9ae0d9eb4ccc 100644
--- a/drivers/net/can/flexcan/flexcan-core.c
+++ b/drivers/net/can/flexcan/flexcan-core.c
@@ -397,6 +397,15 @@ static const struct flexcan_devtype_data nxp_s32g2_devtype_data = {
FLEXCAN_QUIRK_SECONDARY_MB_IRQ,
};
+static const struct flexcan_devtype_data nxp_s32n_devtype_data = {
+ .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
+ FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_RX_MAILBOX |
+ FLEXCAN_QUIRK_BROKEN_PERR_STATE | FLEXCAN_QUIRK_SUPPORT_FD |
+ FLEXCAN_QUIRK_SUPPORT_ECC | FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX |
+ FLEXCAN_QUIRK_SUPPORT_RX_MAILBOX_RTR |
+ FLEXCAN_QUIRK_IRQ_BERR,
+};
+
static const struct can_bittiming_const flexcan_bittiming_const = {
.name = DRV_NAME,
.tseg1_min = 4,
@@ -2232,6 +2241,7 @@ static const struct of_device_id flexcan_of_match[] = {
{ .compatible = "fsl,ls1021ar2-flexcan", .data = &fsl_ls1021a_r2_devtype_data, },
{ .compatible = "fsl,lx2160ar1-flexcan", .data = &fsl_lx2160a_r1_devtype_data, },
{ .compatible = "nxp,s32g2-flexcan", .data = &nxp_s32g2_devtype_data, },
+ { .compatible = "nxp,s32n79-flexcan", .data = &nxp_s32n_devtype_data, },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, flexcan_of_match);
--
2.43.0
^ permalink raw reply related
* [PATCH RESEND v4 7/8] arm64: dts: s32n79: add FlexCAN nodes
From: Ciprian Costea @ 2026-04-21 10:26 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
Fabio Estevam
Cc: Pengutronix Kernel Team, linux-can, devicetree, linux-kernel, imx,
linux-arm-kernel, NXP S32 Linux Team, Christophe Lizzi,
Alberto Ruiz, Enric Balletbo, Eric Chanudet,
Ciprian Marian Costea, Andra-Teodora Ilie
In-Reply-To: <20260421102603.4122332-1-ciprianmarian.costea@oss.nxp.com>
From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
The S32N79 integrates multiple FlexCAN instances connected through the RCU
irqsteer interrupt controller.
Co-developed-by: Andra-Teodora Ilie <andra.ilie@nxp.com>
Signed-off-by: Andra-Teodora Ilie <andra.ilie@nxp.com>
Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
---
arch/arm64/boot/dts/freescale/s32n79.dtsi | 50 +++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/s32n79.dtsi b/arch/arm64/boot/dts/freescale/s32n79.dtsi
index 94ab58783fdc..c1a4fdead91d 100644
--- a/arch/arm64/boot/dts/freescale/s32n79.dtsi
+++ b/arch/arm64/boot/dts/freescale/s32n79.dtsi
@@ -352,6 +352,56 @@ pmu: pmu {
interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_HIGH>;
};
+ rcu-bus {
+ compatible = "simple-bus";
+ ranges = <0x54000000 0x0 0x54000000 0x4000000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ irqsteer_rcu: interrupt-controller@55101000 {
+ compatible = "nxp,s32n79-irqsteer";
+ reg = <0x55101000 0x1000>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks 0xf9>;
+ clock-names = "ipg";
+ fsl,channel = <0>;
+ fsl,num-irqs = <512>;
+ status = "disabled";
+ };
+
+ can0: can@55b60000 {
+ compatible = "nxp,s32n79-flexcan";
+ reg = <0x55b60000 0x4000>;
+ interrupt-parent = <&irqsteer_rcu>;
+ interrupts = <0>, <64>;
+ interrupt-names = "mb-0", "berr";
+ clocks = <&clks 0xf9>, <&clks 0xfc>;
+ clock-names = "ipg", "per";
+ status = "disabled";
+ };
+
+ can1: can@55b70000 {
+ compatible = "nxp,s32n79-flexcan";
+ reg = <0x55b70000 0x4000>;
+ interrupt-parent = <&irqsteer_rcu>;
+ interrupts = <1>, <65>;
+ interrupt-names = "mb-0", "berr";
+ clocks = <&clks 0xf9>, <&clks 0xfc>;
+ clock-names = "ipg", "per";
+ status = "disabled";
+ };
+ };
+
timer: timer {
compatible = "arm,armv8-timer";
interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
--
2.43.0
^ permalink raw reply related
* [PATCH RESEND v4 8/8] arm64: dts: s32n79: enable FlexCAN devices
From: Ciprian Costea @ 2026-04-21 10:26 UTC (permalink / raw)
To: Marc Kleine-Budde, Vincent Mailhol, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Frank Li, Sascha Hauer,
Fabio Estevam
Cc: Pengutronix Kernel Team, linux-can, devicetree, linux-kernel, imx,
linux-arm-kernel, NXP S32 Linux Team, Christophe Lizzi,
Alberto Ruiz, Enric Balletbo, Eric Chanudet,
Ciprian Marian Costea
In-Reply-To: <20260421102603.4122332-1-ciprianmarian.costea@oss.nxp.com>
From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
Enable FlexCAN controller instances (can0 and can1) and the required RCU
irqsteer interrupt controller on S32N79-RDB board.
Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
---
arch/arm64/boot/dts/freescale/s32n79-rdb.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/s32n79-rdb.dts b/arch/arm64/boot/dts/freescale/s32n79-rdb.dts
index 1feccd61258e..65a595d7535f 100644
--- a/arch/arm64/boot/dts/freescale/s32n79-rdb.dts
+++ b/arch/arm64/boot/dts/freescale/s32n79-rdb.dts
@@ -43,10 +43,22 @@ memory@80000000 {
};
};
+&can0 {
+ status = "okay";
+};
+
+&can1 {
+ status = "okay";
+};
+
&irqsteer_coss {
status = "okay";
};
+&irqsteer_rcu {
+ status = "okay";
+};
+
&uart0 {
status = "okay";
};
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v5 04/12] coresight: etm4x: exclude ss_status from drvdata->config
From: Yeoreum Yun @ 2026-04-21 10:30 UTC (permalink / raw)
To: Mike Leach
Cc: Suzuki K Poulose, Leo Yan, coresight, linux-arm-kernel,
linux-kernel, james.clark, alexander.shishkin, jie.gan
In-Reply-To: <aedLaFhwyYMM8tUw@e129823.arm.com>
> Hi Mike,
>
> > Hi,
> >
> > This register [bit 31] indicates if a single shot comparator has matched. So
> > read-back provides information to the user post run to determine which if
> > any of the comparators set in this way has actually matched.
>
> Okay. so after disable sysfs session, to check former session
> check whether comprator has matched.
>
> >
> > Moreover, the specification states "Software must reset this bit to 0 to
> > re-enable single-shot control" and "Reset state is unknown. STATUS must be
> > written to set an initial state...."
> >
> > Therefore this register must be written as part of any configuration so
> > should be available in the drvdata->config for both read and write,
>
> But I don't think this is the reason for locate ss_status into "config"
> since its write purpose is not to configure but the "clear" former bit.
> That's why I think it's enough to clear when the new sysfs session starts.
>
IOW, I think it's better to remove ss_status from configfs item
and
- add field ss_cmp in etm4_cpas
- add another field ss_status under "etm4_drvdata" to show "PENDING
and STATUS" bits to sysfs after finishing session.
Is is valid for you?
> --
> Sincerely,
> Yeoreum Yun
>
--
Sincerely,
Yeoreum Yun
^ permalink raw reply
* [PATCH v3 0/3] gpio: Add EIO GPIO support
From: Shubhrajyoti Datta @ 2026-04-21 10:43 UTC (permalink / raw)
To: linux-kernel
Cc: git, shubhrajyoti.datta, Shubhrajyoti Datta, Srinivas Neeli,
Michal Simek, Linus Walleij, Bartosz Golaszewski, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-gpio, devicetree,
linux-arm-kernel
Add the EIO GPIO support.
Add the dt description and the compatible to the driver.
Changes in v3:
- Update the commit message
Changes in v2:
- Add new patch to sort the compatible strings alphabetically
- Add description of EIO block
Shubhrajyoti Datta (3):
dt-bindings: gpio: zynq: Sort compatible strings alphabetically
dt-bindings: gpio: Add EIO GPIO compatible to gpio-zynq
gpio: zynq: Add eio gpio support
.../devicetree/bindings/gpio/gpio-zynq.yaml | 18 +++++++++++++++---
drivers/gpio/gpio-zynq.c | 12 ++++++++++++
2 files changed, 27 insertions(+), 3 deletions(-)
--
2.34.1
^ permalink raw reply
* [PATCH v3 1/3] dt-bindings: gpio: zynq: Sort compatible strings alphabetically
From: Shubhrajyoti Datta @ 2026-04-21 10:43 UTC (permalink / raw)
To: linux-kernel
Cc: git, shubhrajyoti.datta, Shubhrajyoti Datta, Srinivas Neeli,
Michal Simek, Linus Walleij, Bartosz Golaszewski, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-gpio, devicetree,
linux-arm-kernel
In-Reply-To: <20260421104358.2496125-1-shubhrajyoti.datta@amd.com>
Sort the compatible string alphabetically.
Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
---
(no changes since v2)
Changes in v2:
- Add new patch to sort the compatible strings alphabetically
Documentation/devicetree/bindings/gpio/gpio-zynq.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml
index 5e2496379a3c..30a7f836c341 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml
+++ b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml
@@ -12,10 +12,10 @@ maintainers:
properties:
compatible:
enum:
+ - xlnx,pmc-gpio-1.0
+ - xlnx,versal-gpio-1.0
- xlnx,zynq-gpio-1.0
- xlnx,zynqmp-gpio-1.0
- - xlnx,versal-gpio-1.0
- - xlnx,pmc-gpio-1.0
reg:
maxItems: 1
--
2.34.1
^ permalink raw reply related
* [PATCH v3 2/3] dt-bindings: gpio: Add EIO GPIO compatible to gpio-zynq
From: Shubhrajyoti Datta @ 2026-04-21 10:43 UTC (permalink / raw)
To: linux-kernel
Cc: git, shubhrajyoti.datta, Shubhrajyoti Datta, Srinivas Neeli,
Michal Simek, Linus Walleij, Bartosz Golaszewski, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-gpio, devicetree,
linux-arm-kernel
In-Reply-To: <20260421104358.2496125-1-shubhrajyoti.datta@amd.com>
EIO (Extended IO) GPIO is a Xilinx IP block that exposes
multiplexed I/O pins through an EIO interface.
The EIO GPIO block has 2 banks with 26 GPIOs each (52 total).
The GPIO width cannot be determined from the hardware registers,
the driver relies on the compatible string to select the correct
bank/pin configuration. A new compatible is therefore required.
The block is currently present on xa2ve3288 silicon.
The compatible string uses version 1.0 matching the IP core version.
Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
---
Changes in v3:
- Update the commit message
Changes in v2:
- Add description of EIO block
.../devicetree/bindings/gpio/gpio-zynq.yaml | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml
index 30a7f836c341..1ca067217509 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml
+++ b/Documentation/devicetree/bindings/gpio/gpio-zynq.yaml
@@ -12,6 +12,7 @@ maintainers:
properties:
compatible:
enum:
+ - xlnx,eio-gpio-1.0
- xlnx,pmc-gpio-1.0
- xlnx,versal-gpio-1.0
- xlnx,zynq-gpio-1.0
@@ -30,7 +31,7 @@ properties:
gpio-line-names:
description: strings describing the names of each gpio line
- minItems: 58
+ minItems: 52
maxItems: 174
interrupt-controller: true
@@ -89,6 +90,17 @@ allOf:
minItems: 116
maxItems: 116
+ - if:
+ properties:
+ compatible:
+ enum:
+ - xlnx,eio-gpio-1.0
+ then:
+ properties:
+ gpio-line-names:
+ minItems: 52
+ maxItems: 52
+
required:
- compatible
- reg
--
2.34.1
^ permalink raw reply related
* [PATCH v3 3/3] gpio: zynq: Add eio gpio support
From: Shubhrajyoti Datta @ 2026-04-21 10:43 UTC (permalink / raw)
To: linux-kernel
Cc: git, shubhrajyoti.datta, Shubhrajyoti Datta, Srinivas Neeli,
Michal Simek, Linus Walleij, Bartosz Golaszewski, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-gpio, devicetree,
linux-arm-kernel
In-Reply-To: <20260421104358.2496125-1-shubhrajyoti.datta@amd.com>
Add support for the EIO GPIO controller found on
xa2ve3288 silicon.
The EIO GPIO block provides access to multiplexed I/O pins exposed
through the EIO interface. Only bank 0 and bank 1 are connected to
external MIO pins, with 26 GPIOs per bank (52 GPIOs total). This
change extends the Zynq GPIO driver to support the EIO GPIO
variant.
Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
---
(no changes since v1)
drivers/gpio/gpio-zynq.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index 571e366624d2..8118ae3412c2 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -25,6 +25,7 @@
#define VERSAL_GPIO_MAX_BANK 4
#define PMC_GPIO_MAX_BANK 5
#define VERSAL_UNUSED_BANKS 2
+#define EIO_GPIO_MAX_BANK 2
#define ZYNQ_GPIO_BANK0_NGPIO 32
#define ZYNQ_GPIO_BANK1_NGPIO 22
@@ -818,6 +819,16 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, zynq_gpio_runtime_resume, NULL)
};
+static const struct zynq_platform_data eio_gpio_def = {
+ .label = "eio_gpio",
+ .ngpio = 52,
+ .max_bank = EIO_GPIO_MAX_BANK,
+ .bank_min[0] = 0,
+ .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */
+ .bank_min[1] = 26,
+ .bank_max[1] = 51, /* Bank 1 are connected to MIOs (26 pins) */
+};
+
static const struct zynq_platform_data versal_gpio_def = {
.label = "versal_gpio",
.quirks = GPIO_QUIRK_VERSAL,
@@ -882,6 +893,7 @@ static const struct of_device_id zynq_gpio_of_match[] = {
{ .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def },
{ .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def },
{ .compatible = "xlnx,pmc-gpio-1.0", .data = &pmc_gpio_def },
+ { .compatible = "xlnx,eio-gpio-1.0", .data = &eio_gpio_def },
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v5 07/12] coresight: etm4x: fix inconsistencies with sysfs configuration
From: Leo Yan @ 2026-04-21 10:46 UTC (permalink / raw)
To: Yeoreum Yun
Cc: coresight, linux-arm-kernel, linux-kernel, suzuki.poulose,
mike.leach, james.clark, alexander.shishkin, jie.gan
In-Reply-To: <20260415165528.3369607-8-yeoreum.yun@arm.com>
On Wed, Apr 15, 2026 at 05:55:23PM +0100, Yeoreum Yun wrote:
> The current ETM4x configuration via sysfs can lead to
> several inconsistencies:
>
> - If the configuration is modified via sysfs while a perf session is
> active, the running configuration may differ before a sched-out and
> after a subsequent sched-in.
>
> - If a perf session and a sysfs session enable tracing concurrently,
> the configuration from configfs may become corrupted.
I think this happens because the sysfs path calls
cscfg_csdev_enable_active_config() without first acquiring the mode,
allowing a perf session to acquire the mode and call the same function
concurrently.
> - There is a risk of corrupting drvdata->config if a perf session enables
> tracing while cscfg_csdev_disable_active_config() is being handled in
> etm4_disable_sysfs().
Similiar to above, cscfg_csdev_disable_active_config() is not
protected in etm4_disable_sysfs().
> To resolve these issues, separate the configuration into:
>
> - active_config: the configuration applied to the current session
> - config: the configuration set via sysfs
>
> Additionally:
>
> - Apply the configuration from configfs after taking the appropriate mode.
>
> - Since active_config and related fields are accessed only by the local CPU
> in etm4_enable/disable_sysfs_smp_call() (similar to perf enable/disable),
> remove the lock/unlock from the sysfs enable/disable path and
> startup/dying_cpu except when to access config fields.
>
> Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
> ---
> .../hwtracing/coresight/coresight-etm4x-cfg.c | 2 +-
> .../coresight/coresight-etm4x-core.c | 107 ++++++++++--------
> drivers/hwtracing/coresight/coresight-etm4x.h | 2 +
> 3 files changed, 63 insertions(+), 48 deletions(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> index d14d7c8a23e5..0553771d04e7 100644
> --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> @@ -47,7 +47,7 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
> struct cscfg_regval_csdev *reg_csdev, u32 offset)
> {
> int err = -EINVAL, idx;
> - struct etmv4_config *drvcfg = &drvdata->config;
> + struct etmv4_config *drvcfg = &drvdata->active_config;
> u32 off_mask;
>
> if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
> index b199aebbdb60..15aaf4a898e1 100644
> --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
> +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
> @@ -245,6 +245,10 @@ void etm4_release_trace_id(struct etmv4_drvdata *drvdata)
>
> struct etm4_enable_arg {
> struct etmv4_drvdata *drvdata;
> + unsigned long cfg_hash;
> + int preset;
Since smp call cannot directly call cscfg_config_sysfs_get_active_cfg()
due to it runs in atomic context but ...active_cfg() tries to acquire
mutex. So we firstly retrieve pass 'cfg_hash' and 'preset' in sleepable
context and deliver them to the SMP call.
After coresight cfg refactoring, we should remove cfg_hash/preset from
ETM driver, the ETM driver only needs to retrieve register list and can
do this in smp call.
Before we finish cfg refactoring, it is fine for me to add these two
parameters into etm4_enable_arg.
> + u8 trace_id;
Can we add 'path' instead ? The SMP call can retrieve path->trace_id.
This can benefit for future clean up (e.g., we can store config into
path so we can retrieve config from path pointer), and this allows us
for further refactoring to unify etm4_enable_sysfs_smp_call() and
etm4_enable_perf().
> + struct etmv4_config config;
We don't need this. We can defer to get drvdata->config in SMP call.
> int rc;
> };
[...]
> @@ -918,40 +946,29 @@ static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_pa
>
> /* enable any config activated by configfs */
> cscfg_config_sysfs_get_active_cfg(&cfg_hash, &preset);
> - if (cfg_hash) {
> - ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset);
> - if (ret) {
> - etm4_release_trace_id(drvdata);
> - return ret;
> - }
> - }
> -
> - raw_spin_lock(&drvdata->spinlock);
> -
> - drvdata->trcid = path->trace_id;
> -
> - /* Tracer will never be paused in sysfs mode */
> - drvdata->paused = false;
>
> /*
> * Executing etm4_enable_hw on the cpu whose ETM is being enabled
> * ensures that register writes occur when cpu is powered.
> */
> arg.drvdata = drvdata;
> + arg.cfg_hash = cfg_hash;
> + arg.preset = preset;
> + arg.trace_id = path->trace_id;
> +
> + raw_spin_lock(&drvdata->spinlock);
> + arg.config = drvdata->config;
> + raw_spin_unlock(&drvdata->spinlock);
Can we defer this in smp call ? And we can consolidate a bit
configurations, we can consider to use a separate patch for this.
etm4x_apply_config(drvdata, event, hash, preset)
{
/* perf mode */
if (event) {
etm4_parse_event_config(drvdata->csdev, event);
} else if (mode == CS_MODE_PERF) {
scoped_guard(raw_spinlock, &drvdata->spinlock)
&drvdata->active_config = drvdata->config;
}
/* At the end, we always apply the config */
cscfg_csdev_enable_active_config(drvdata->csdev, hash, preset);
}
> +
> ret = smp_call_function_single(drvdata->cpu,
> etm4_enable_sysfs_smp_call, &arg, 1);
> if (!ret)
> ret = arg.rc;
> if (!ret)
> - drvdata->sticky_enable = true;
> -
> - if (ret)
> + dev_dbg(&csdev->dev, "ETM tracing enabled\n");
> + else
> etm4_release_trace_id(drvdata);
>
> - raw_spin_unlock(&drvdata->spinlock);
> -
> - if (!ret)
> - dev_dbg(&csdev->dev, "ETM tracing enabled\n");
> return ret;
> }
>
> @@ -1038,7 +1055,7 @@ static void etm4_disable_hw(struct etmv4_drvdata *drvdata)
> {
> u32 control;
> const struct etmv4_caps *caps = &drvdata->caps;
> - struct etmv4_config *config = &drvdata->config;
> + struct etmv4_config *config = &drvdata->active_config;
> struct coresight_device *csdev = drvdata->csdev;
> struct csdev_access *csa = &csdev->access;
> int i;
> @@ -1074,6 +1091,8 @@ static void etm4_disable_sysfs_smp_call(void *info)
>
> etm4_disable_hw(drvdata);
>
> + cscfg_csdev_disable_active_config(drvdata->csdev);
> +
> coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
> }
>
> @@ -1124,7 +1143,6 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
> * DYING hotplug callback is serviced by the ETM driver.
> */
> cpus_read_lock();
> - raw_spin_lock(&drvdata->spinlock);
>
> /*
> * Executing etm4_disable_hw on the cpu whose ETM is being disabled
> @@ -1133,10 +1151,6 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
> smp_call_function_single(drvdata->cpu, etm4_disable_sysfs_smp_call,
> drvdata, 1);
>
> - raw_spin_unlock(&drvdata->spinlock);
> -
> - cscfg_csdev_disable_active_config(csdev);
> -
> cpus_read_unlock();
>
> /*
> @@ -1379,6 +1393,7 @@ static void etm4_init_arch_data(void *info)
> struct etm4_init_arg *init_arg = info;
> struct etmv4_drvdata *drvdata;
> struct etmv4_caps *caps;
> + struct etmv4_config *config;
> struct csdev_access *csa;
> struct device *dev = init_arg->dev;
> int i;
> @@ -1386,6 +1401,7 @@ static void etm4_init_arch_data(void *info)
> drvdata = dev_get_drvdata(init_arg->dev);
> caps = &drvdata->caps;
> csa = init_arg->csa;
> + config = &drvdata->active_config;
Should we init drvdata->config instead so make it has sane values ?
In other words, drvdata->active_config are always set at the runtime,
so don't need to init it at all, right?
Thanks,
Leo
^ permalink raw reply
* [PATCH 1/4] arm64: dts: imx8mp: switch usb controller to flattened model
From: Xu Yang @ 2026-04-21 10:55 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, jun.li
Switch to use flattened model for all USB controllers. To enable USB
controllers with restricted DMA access range to work correctly, add a
pseudo simple-bus to constrain the dma address.
Also reorder USB-related nodes.
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8mp.dtsi | 161 ++++++++++------------
1 file changed, 76 insertions(+), 85 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
index 90d7bb8f5619..8b3aab14ccf1 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
@@ -2329,6 +2329,82 @@ gpu2d: gpu@38008000 {
power-domains = <&pgc_gpu2d>;
};
+ bus@38100000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ dma-ranges = <0x40000000 0x40000000 0xc0000000>;
+ ranges;
+
+ usb3_0: usb_dwc3_0: usb@38100000 {
+ compatible = "nxp,imx8mp-dwc3";
+ reg = <0x38100000 0x10000>,
+ <0x32f10100 0x8>,
+ <0x381f0000 0x20>;
+ reg-names = "core", "blkctl", "glue";
+ clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
+ <&clk IMX8MP_CLK_USB_ROOT>,
+ <&clk IMX8MP_CLK_USB_CORE_REF>,
+ <&clk IMX8MP_CLK_USB_SUSP>;
+ clock-names = "hsio", "bus_early", "ref", "suspend";
+ interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dwc_usb3", "wakeup";
+ power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_USB>;
+ phys = <&usb3_phy0>, <&usb3_phy0>;
+ phy-names = "usb2-phy", "usb3-phy";
+ snps,gfladj-refclk-lpm-sel-quirk;
+ snps,parkmode-disable-ss-quirk;
+ status = "disabled";
+ };
+
+ usb3_1: usb_dwc3_1: usb@38200000 {
+ compatible = "nxp,imx8mp-dwc3";
+ reg = <0x38200000 0x10000>,
+ <0x32f10108 0x8>,
+ <0x382f0000 0x20>;
+ reg-names = "core", "blkctl", "glue";
+ clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
+ <&clk IMX8MP_CLK_USB_ROOT>,
+ <&clk IMX8MP_CLK_USB_CORE_REF>,
+ <&clk IMX8MP_CLK_USB_SUSP>;
+ clock-names = "hsio", "bus_early", "ref", "suspend";
+ interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dwc_usb3", "wakeup";
+ power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_USB>;
+ phys = <&usb3_phy1>, <&usb3_phy1>;
+ phy-names = "usb2-phy", "usb3-phy";
+ snps,gfladj-refclk-lpm-sel-quirk;
+ snps,parkmode-disable-ss-quirk;
+ status = "disabled";
+ };
+ };
+
+ usb3_phy0: usb-phy@381f0040 {
+ compatible = "fsl,imx8mp-usb-phy";
+ reg = <0x381f0040 0x40>;
+ clocks = <&clk IMX8MP_CLK_USB_PHY_ROOT>;
+ clock-names = "phy";
+ assigned-clocks = <&clk IMX8MP_CLK_USB_PHY_REF>;
+ assigned-clock-parents = <&clk IMX8MP_CLK_24M>;
+ power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_USB_PHY1>;
+ #phy-cells = <0>;
+ status = "disabled";
+ };
+
+ usb3_phy1: usb-phy@382f0040 {
+ compatible = "fsl,imx8mp-usb-phy";
+ reg = <0x382f0040 0x40>;
+ clocks = <&clk IMX8MP_CLK_USB_PHY_ROOT>;
+ clock-names = "phy";
+ assigned-clocks = <&clk IMX8MP_CLK_USB_PHY_REF>;
+ assigned-clock-parents = <&clk IMX8MP_CLK_24M>;
+ power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_USB_PHY2>;
+ #phy-cells = <0>;
+ status = "disabled";
+ };
+
vpu_g1: video-codec@38300000 {
compatible = "nxp,imx8mm-vpu-g1";
reg = <0x38300000 0x10000>;
@@ -2407,91 +2483,6 @@ ddr-pmu@3d800000 {
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
};
- usb3_phy0: usb-phy@381f0040 {
- compatible = "fsl,imx8mp-usb-phy";
- reg = <0x381f0040 0x40>;
- clocks = <&clk IMX8MP_CLK_USB_PHY_ROOT>;
- clock-names = "phy";
- assigned-clocks = <&clk IMX8MP_CLK_USB_PHY_REF>;
- assigned-clock-parents = <&clk IMX8MP_CLK_24M>;
- power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_USB_PHY1>;
- #phy-cells = <0>;
- status = "disabled";
- };
-
- usb3_0: usb@32f10100 {
- compatible = "fsl,imx8mp-dwc3";
- reg = <0x32f10100 0x8>,
- <0x381f0000 0x20>;
- clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
- <&clk IMX8MP_CLK_USB_SUSP>;
- clock-names = "hsio", "suspend";
- interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
- power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_USB>;
- #address-cells = <1>;
- #size-cells = <1>;
- dma-ranges = <0x40000000 0x40000000 0xc0000000>;
- ranges;
- status = "disabled";
-
- usb_dwc3_0: usb@38100000 {
- compatible = "snps,dwc3";
- reg = <0x38100000 0x10000>;
- clocks = <&clk IMX8MP_CLK_USB_ROOT>,
- <&clk IMX8MP_CLK_USB_CORE_REF>,
- <&clk IMX8MP_CLK_USB_SUSP>;
- clock-names = "bus_early", "ref", "suspend";
- interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
- phys = <&usb3_phy0>, <&usb3_phy0>;
- phy-names = "usb2-phy", "usb3-phy";
- snps,gfladj-refclk-lpm-sel-quirk;
- snps,parkmode-disable-ss-quirk;
- };
-
- };
-
- usb3_phy1: usb-phy@382f0040 {
- compatible = "fsl,imx8mp-usb-phy";
- reg = <0x382f0040 0x40>;
- clocks = <&clk IMX8MP_CLK_USB_PHY_ROOT>;
- clock-names = "phy";
- assigned-clocks = <&clk IMX8MP_CLK_USB_PHY_REF>;
- assigned-clock-parents = <&clk IMX8MP_CLK_24M>;
- power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_USB_PHY2>;
- #phy-cells = <0>;
- status = "disabled";
- };
-
- usb3_1: usb@32f10108 {
- compatible = "fsl,imx8mp-dwc3";
- reg = <0x32f10108 0x8>,
- <0x382f0000 0x20>;
- clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
- <&clk IMX8MP_CLK_USB_SUSP>;
- clock-names = "hsio", "suspend";
- interrupts = <GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>;
- power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_USB>;
- #address-cells = <1>;
- #size-cells = <1>;
- dma-ranges = <0x40000000 0x40000000 0xc0000000>;
- ranges;
- status = "disabled";
-
- usb_dwc3_1: usb@38200000 {
- compatible = "snps,dwc3";
- reg = <0x38200000 0x10000>;
- clocks = <&clk IMX8MP_CLK_USB_ROOT>,
- <&clk IMX8MP_CLK_USB_CORE_REF>,
- <&clk IMX8MP_CLK_USB_SUSP>;
- clock-names = "bus_early", "ref", "suspend";
- interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
- phys = <&usb3_phy1>, <&usb3_phy1>;
- phy-names = "usb2-phy", "usb3-phy";
- snps,gfladj-refclk-lpm-sel-quirk;
- snps,parkmode-disable-ss-quirk;
- };
- };
-
dsp: dsp@3b6e8000 {
compatible = "fsl,imx8mp-hifi4";
reg = <0x3b6e8000 0x88000>;
--
2.34.1
^ permalink raw reply related
* [PATCH 2/4] arm64: dts: imx95: switch usb3 controller to flattened model
From: Xu Yang @ 2026-04-21 10:55 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, jun.li
In-Reply-To: <20260421105503.1416566-1-xu.yang_2@nxp.com>
Switch to use flattened model for USB3 controller. To enable USB
controller with restricted DMA access range to work correctly, add a
pseudo simple-bus to constrain the dma address.
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
arch/arm64/boot/dts/freescale/imx95.dtsi | 48 ++++++++++++------------
1 file changed, 24 insertions(+), 24 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi
index 71394871d8dd..91048501a692 100644
--- a/arch/arm64/boot/dts/freescale/imx95.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx95.dtsi
@@ -1772,45 +1772,45 @@ smmu: iommu@490d0000 {
};
};
- usb3: usb@4c010010 {
- compatible = "fsl,imx95-dwc3", "fsl,imx8mp-dwc3";
- reg = <0x0 0x4c010010 0x0 0x04>,
- <0x0 0x4c1f0000 0x0 0x20>;
- clocks = <&scmi_clk IMX95_CLK_HSIO>,
- <&scmi_clk IMX95_CLK_32K>;
- clock-names = "hsio", "suspend";
- interrupts = <GIC_SPI 173 IRQ_TYPE_LEVEL_HIGH>;
+ hsio_blk_ctl: syscon@4c0100c0 {
+ compatible = "nxp,imx95-hsio-blk-ctl", "syscon";
+ reg = <0x0 0x4c0100c0 0x0 0x1>;
+ #clock-cells = <1>;
+ clocks = <&clk_sys100m>;
+ power-domains = <&scmi_devpd IMX95_PD_HSIO_TOP>;
+ };
+
+ bus@4c100000 {
+ compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <2>;
- ranges;
- power-domains = <&scmi_devpd IMX95_PD_HSIO_TOP>;
dma-ranges = <0x0 0x0 0x0 0x0 0x10 0x0>;
- status = "disabled";
+ ranges;
- usb3_dwc3: usb@4c100000 {
- compatible = "snps,dwc3";
- reg = <0x0 0x4c100000 0x0 0x10000>;
+ usb3: usb3_dwc3: usb@4c100000 {
+ compatible = "nxp,imx8mp-dwc3";
+ reg = <0x0 0x4c100000 0x0 0x10000>,
+ <0x0 0x4c010010 0x0 0x04>,
+ <0x0 0x4c1f0000 0x0 0x20>;
+ reg-names = "core", "blkctl", "glue";
clocks = <&scmi_clk IMX95_CLK_HSIO>,
+ <&scmi_clk IMX95_CLK_HSIO>,
<&scmi_clk IMX95_CLK_24M>,
<&scmi_clk IMX95_CLK_32K>;
- clock-names = "bus_early", "ref", "suspend";
- interrupts = <GIC_SPI 175 IRQ_TYPE_LEVEL_HIGH>;
+ clock-names = "hsio", "bus_early", "ref", "suspend";
+ interrupts = <GIC_SPI 175 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 173 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dwc_usb3", "wakeup";
+ power-domains = <&scmi_devpd IMX95_PD_HSIO_TOP>;
phys = <&usb3_phy>, <&usb3_phy>;
phy-names = "usb2-phy", "usb3-phy";
snps,gfladj-refclk-lpm-sel-quirk;
snps,parkmode-disable-ss-quirk;
iommus = <&smmu 0xe>;
+ status = "disabled";
};
};
- hsio_blk_ctl: syscon@4c0100c0 {
- compatible = "nxp,imx95-hsio-blk-ctl", "syscon";
- reg = <0x0 0x4c0100c0 0x0 0x1>;
- #clock-cells = <1>;
- clocks = <&clk_sys100m>;
- power-domains = <&scmi_devpd IMX95_PD_HSIO_TOP>;
- };
-
usb3_phy: phy@4c1f0040 {
compatible = "fsl,imx95-usb-phy", "fsl,imx8mp-usb-phy";
reg = <0x0 0x4c1f0040 0x0 0x40>,
--
2.34.1
^ permalink raw reply related
* [PATCH 3/4] arm64: dts: imx8mp-evk: add typec node
From: Xu Yang @ 2026-04-21 10:55 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, jun.li
In-Reply-To: <20260421105503.1416566-1-xu.yang_2@nxp.com>
The first USB port features a Type-C connector with dual data role
and dual power role capabilities. Add the Type-C device node and
enable the corresponding USB controller node.
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8mp-evk.dts | 67 ++++++++++++++++++++
1 file changed, 67 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
index d0a2bd975a18..1d9e9a8f5e5b 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
@@ -6,6 +6,7 @@
/dts-v1/;
#include <dt-bindings/phy/phy-imx8-pcie.h>
+#include <dt-bindings/usb/pd.h>
#include "imx8mp.dtsi"
/ {
@@ -636,6 +637,35 @@ adv7535_out: endpoint {
};
};
+
+ ptn5110: tcpc@50 {
+ compatible = "nxp,ptn5110", "tcpci";
+ reg = <0x50>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_typec>;
+ interrupt-parent = <&gpio4>;
+ interrupts = <19 IRQ_TYPE_LEVEL_LOW>;
+ orientation-gpios = <&gpio4 20 GPIO_ACTIVE_HIGH>;
+
+ usb_con: connector {
+ compatible = "usb-c-connector";
+ label = "USB-C";
+ power-role = "dual";
+ data-role = "dual";
+ try-power-role = "sink";
+ source-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>;
+ sink-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)
+ PDO_VAR(5000, 20000, 3000)>;
+ op-sink-microwatt = <15000000>;
+ self-powered;
+
+ port {
+ typec_con_hs: endpoint {
+ remote-endpoint = <&usb3_data_hs>;
+ };
+ };
+ };
+ };
};
&i2c3 {
@@ -846,7 +876,37 @@ &uart2 {
status = "okay";
};
+&usb3_phy0 {
+ fsl,phy-tx-vref-tune-percent = <122>;
+ fsl,phy-tx-preemp-amp-tune-microamp = <1800>;
+ fsl,phy-tx-vboost-level-microvolt = <1156>;
+ fsl,phy-comp-dis-tune-percent = <115>;
+ fsl,phy-pcs-tx-deemph-3p5db-attenuation-db = <33>;
+ fsl,phy-pcs-tx-swing-full-percent = <100>;
+ status = "okay";
+};
+
+&usb3_0 {
+ dr_mode = "otg";
+ hnp-disable;
+ srp-disable;
+ adp-disable;
+ usb-role-switch;
+ role-switch-default-mode = "peripheral";
+ snps,dis-u1-entry-quirk;
+ snps,dis-u2-entry-quirk;
+ status = "okay";
+
+ port {
+ usb3_data_hs: endpoint {
+ remote-endpoint = <&typec_con_hs>;
+ };
+ };
+};
+
&usb3_phy1 {
+ fsl,phy-tx-preemp-amp-tune-microamp = <1800>;
+ fsl,phy-tx-vref-tune-percent = <116>;
status = "okay";
};
@@ -1174,6 +1234,13 @@ MX8MP_IOMUXC_UART2_TXD__UART2_DCE_TX 0x140
>;
};
+ pinctrl_typec: typecgrp {
+ fsl,pins = <
+ MX8MP_IOMUXC_SAI1_TXD7__GPIO4_IO19 0x1c4
+ MX8MP_IOMUXC_SAI1_MCLK__GPIO4_IO20 0x16
+ >;
+ };
+
pinctrl_usb1_vbus: usb1grp {
fsl,pins = <
MX8MP_IOMUXC_GPIO1_IO14__USB2_OTG_PWR 0x10
--
2.34.1
^ permalink raw reply related
* [PATCH 4/4] arm64: dts: imx8mq-evk: add typec node
From: Xu Yang @ 2026-04-21 10:55 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, jun.li
In-Reply-To: <20260421105503.1416566-1-xu.yang_2@nxp.com>
The first USB port features a Type-C connector with dual data role
and dual power role capabilities. Add the Type-C device node and
enable the corresponding USB controller node.
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8mq-evk.dts | 59 ++++++++++++++++++++
1 file changed, 59 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8mq-evk.dts b/arch/arm64/boot/dts/freescale/imx8mq-evk.dts
index d48f901487d4..1b93d80744be 100644
--- a/arch/arm64/boot/dts/freescale/imx8mq-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mq-evk.dts
@@ -6,6 +6,7 @@
/dts-v1/;
+#include <dt-bindings/usb/pd.h>
#include "imx8mq.dtsi"
/ {
@@ -330,6 +331,35 @@ vgen6_reg: vgen6 {
};
};
};
+
+ ptn5110: tcpc@50 {
+ compatible = "nxp,ptn5110", "tcpci";
+ reg = <0x50>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_typec>;
+ interrupt-parent = <&gpio3>;
+ interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+ orientation-gpios = <&gpio3 15 GPIO_ACTIVE_LOW>;
+
+ usb_con: connector {
+ compatible = "usb-c-connector";
+ label = "USB-C";
+ power-role = "dual";
+ data-role = "dual";
+ try-power-role = "sink";
+ source-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>;
+ sink-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)
+ PDO_VAR(5000, 20000, 3000)>;
+ op-sink-microwatt = <15000000>;
+ self-powered;
+
+ port {
+ typec_con_hs: endpoint {
+ remote-endpoint = <&usb3_data_hs>;
+ };
+ };
+ };
+ };
};
&lcdif {
@@ -488,6 +518,28 @@ &uart1 {
status = "okay";
};
+&usb3_phy0 {
+ status = "okay";
+};
+
+&usb_dwc3_0 {
+ dr_mode = "otg";
+ hnp-disable;
+ srp-disable;
+ adp-disable;
+ usb-role-switch;
+ role-switch-default-mode = "peripheral";
+ snps,dis-u1-entry-quirk;
+ snps,dis-u2-entry-quirk;
+ status = "okay";
+
+ port {
+ usb3_data_hs: endpoint {
+ remote-endpoint = <&typec_con_hs>;
+ };
+ };
+};
+
&usb3_phy1 {
status = "okay";
};
@@ -640,6 +692,13 @@ MX8MQ_IOMUXC_SPDIF_RX_SPDIF1_IN 0xd6
>;
};
+ pinctrl_typec: typecgrp {
+ fsl,pins = <
+ MX8MQ_IOMUXC_NAND_CE2_B_GPIO3_IO3 0x17059
+ MX8MQ_IOMUXC_NAND_RE_B_GPIO3_IO15 0x16
+ >;
+ };
+
pinctrl_uart1: uart1grp {
fsl,pins = <
MX8MQ_IOMUXC_UART1_RXD_UART1_DCE_RX 0x49
--
2.34.1
^ permalink raw reply related
* [PATCH v17 1/7] coresight: core: refactor ctcu_get_active_port and make it generic
From: Jie Gan @ 2026-04-21 10:55 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
Bjorn Andersson, Konrad Dybcio
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
devicetree, Jie Gan, Mike Leach
In-Reply-To: <20260421-enable-byte-cntr-for-ctcu-v17-0-9cf36ff55fc0@oss.qualcomm.com>
Remove ctcu_get_active_port from CTCU module and add it to the core
framework.
The port number is crucial for the CTCU device to identify which ETR
it serves. With the port number we can correctly get required parameters
of the CTCU device in TMC module.
Reviewed-by: Mike Leach <mike.leach@linaro.org>
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 24 +++++++++++++++++++++++
drivers/hwtracing/coresight/coresight-ctcu-core.c | 19 +-----------------
drivers/hwtracing/coresight/coresight-priv.h | 2 ++
3 files changed, 27 insertions(+), 18 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 46f247f73cf6..221de57ca57b 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -588,6 +588,30 @@ struct coresight_device *coresight_get_sink(struct coresight_path *path)
}
EXPORT_SYMBOL_GPL(coresight_get_sink);
+/**
+ * coresight_get_in_port: Find the input port number at @remote where the @csdev
+ * device is connected to.
+ *
+ * @csdev: csdev of the device.
+ * @remote: csdev of the remote device which is connected to @csdev.
+ *
+ * Return: port number upon success or -EINVAL for fail.
+ */
+int coresight_get_in_port(struct coresight_device *csdev,
+ struct coresight_device *remote)
+{
+ struct coresight_platform_data *pdata = remote->pdata;
+ int i;
+
+ for (i = 0; i < pdata->nr_inconns; ++i) {
+ if (pdata->in_conns[i]->src_dev == csdev)
+ return pdata->in_conns[i]->dest_port;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(coresight_get_in_port);
+
u32 coresight_get_sink_id(struct coresight_device *csdev)
{
if (!csdev->ea)
diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c
index 9043cad42f01..e8720026c9e3 100644
--- a/drivers/hwtracing/coresight/coresight-ctcu-core.c
+++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c
@@ -116,23 +116,6 @@ static int __ctcu_set_etr_traceid(struct coresight_device *csdev, u8 traceid, in
return 0;
}
-/*
- * Searching the sink device from helper's view in case there are multiple helper devices
- * connected to the sink device.
- */
-static int ctcu_get_active_port(struct coresight_device *sink, struct coresight_device *helper)
-{
- struct coresight_platform_data *pdata = helper->pdata;
- int i;
-
- for (i = 0; i < pdata->nr_inconns; ++i) {
- if (pdata->in_conns[i]->src_dev == sink)
- return pdata->in_conns[i]->dest_port;
- }
-
- return -EINVAL;
-}
-
static int ctcu_set_etr_traceid(struct coresight_device *csdev, struct coresight_path *path,
bool enable)
{
@@ -145,7 +128,7 @@ static int ctcu_set_etr_traceid(struct coresight_device *csdev, struct coresight
return -EINVAL;
}
- port_num = ctcu_get_active_port(sink, csdev);
+ port_num = coresight_get_in_port(sink, csdev);
if (port_num < 0)
return -EINVAL;
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 1ea882dffd70..5532ec82e82c 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -155,6 +155,8 @@ void coresight_remove_links(struct coresight_device *orig,
u32 coresight_get_sink_id(struct coresight_device *csdev);
void coresight_path_assign_trace_id(struct coresight_path *path,
enum cs_mode mode);
+int coresight_get_in_port(struct coresight_device *csdev,
+ struct coresight_device *remote);
#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM3X)
int etm_readl_cp14(u32 off, unsigned int *val);
--
2.34.1
^ permalink raw reply related
* [PATCH v17 2/7] coresight: tmc: add create/clean functions for etr_buf_list
From: Jie Gan @ 2026-04-21 10:55 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
Bjorn Andersson, Konrad Dybcio
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
devicetree, Jie Gan, Mike Leach
In-Reply-To: <20260421-enable-byte-cntr-for-ctcu-v17-0-9cf36ff55fc0@oss.qualcomm.com>
Introduce functions for creating and inserting or removing the
etr_buf_node to/from the etr_buf_list.
The byte-cntr functionality requires two etr_buf to receive trace data.
The active etr_buf collects the trace data from source device, while the
byte-cntr reading function accesses the deactivated etr_buf after is
has been filled and synced, transferring data to the userspace.
Reviewed-by: Mike Leach <mike.leach@linaro.org>
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-tmc-core.c | 1 +
drivers/hwtracing/coresight/coresight-tmc-etr.c | 99 ++++++++++++++++++++++++
drivers/hwtracing/coresight/coresight-tmc.h | 17 ++++
3 files changed, 117 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
index c89fe996af23..bac3278ef4dd 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-core.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
@@ -835,6 +835,7 @@ static int __tmc_probe(struct device *dev, struct resource *res)
idr_init(&drvdata->idr);
mutex_init(&drvdata->idr_mutex);
dev_list = "tmc_etr";
+ INIT_LIST_HEAD(&drvdata->etr_buf_list);
break;
case TMC_CONFIG_TYPE_ETF:
desc.groups = coresight_etf_groups;
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 4dc1defe27a5..ac704617097c 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1918,6 +1918,105 @@ const struct coresight_ops tmc_etr_cs_ops = {
.panic_ops = &tmc_etr_sync_ops,
};
+/**
+ * tmc_clean_etr_buf_list - clean the etr_buf_list.
+ * @drvdata: driver data of the TMC device.
+ *
+ * Remove unused buffers from @drvdata->etr_buf_list and free them.
+ */
+void tmc_clean_etr_buf_list(struct tmc_drvdata *drvdata)
+{
+ struct etr_buf_node *nd, *next;
+
+ list_for_each_entry_safe(nd, next, &drvdata->etr_buf_list, link) {
+ if (nd->sysfs_buf == drvdata->sysfs_buf) {
+ if (coresight_get_mode(drvdata->csdev) != CS_MODE_DISABLED) {
+ /*
+ * Dont free the sysfs_buf, just remove it from the list.
+ * drvdata->sysfs_buf will hold the buffer and free it later.
+ */
+ nd->sysfs_buf = NULL;
+ list_del(&nd->link);
+ kfree(nd);
+ continue;
+ }
+ /* Free the sysfs_buf in coming steps through nd->sysfs_buf */
+ drvdata->sysfs_buf = NULL;
+ }
+ /* Free allocated buffers which are not utilized by ETR */
+ tmc_etr_free_sysfs_buf(nd->sysfs_buf);
+ nd->sysfs_buf = NULL;
+ list_del(&nd->link);
+ kfree(nd);
+ }
+}
+EXPORT_SYMBOL_GPL(tmc_clean_etr_buf_list);
+
+/**
+ * tmc_create_etr_buf_list - create a list to manage the etr_buf_node.
+ * @drvdata: driver data of the TMC device.
+ * @num_nodes: number of nodes want to create with the list.
+ *
+ * Return 0 upon success and return the error number if fail.
+ */
+int tmc_create_etr_buf_list(struct tmc_drvdata *drvdata, int num_nodes)
+{
+ struct etr_buf_node *new_node;
+ struct etr_buf *sysfs_buf;
+ int i = 0, ret = 0;
+
+ /* We dont need a list if there is only one node */
+ if (num_nodes < 2)
+ return -EINVAL;
+
+ /* We expect that sysfs_buf in drvdata has already been allocated. */
+ if (drvdata->sysfs_buf) {
+ /* Directly insert the allocated sysfs_buf into the list first */
+ new_node = kzalloc_obj(*new_node, GFP_KERNEL);
+ if (!new_node)
+ return -ENOMEM;
+
+ new_node->sysfs_buf = drvdata->sysfs_buf;
+ new_node->is_free = false;
+ list_add(&new_node->link, &drvdata->etr_buf_list);
+ i++;
+ }
+
+ while (i < num_nodes) {
+ new_node = kzalloc_obj(*new_node, GFP_KERNEL);
+ if (!new_node) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ sysfs_buf = tmc_alloc_etr_buf(drvdata, drvdata->size, 0, cpu_to_node(0), NULL);
+ if (!sysfs_buf) {
+ kfree(new_node);
+ ret = -ENOMEM;
+ break;
+ }
+
+ /* We dont have a available sysfs_buf in drvdata, setup one */
+ if (!drvdata->sysfs_buf) {
+ drvdata->sysfs_buf = sysfs_buf;
+ new_node->is_free = false;
+ } else {
+ new_node->is_free = true;
+ }
+
+ new_node->sysfs_buf = sysfs_buf;
+ list_add_tail(&new_node->link, &drvdata->etr_buf_list);
+ i++;
+ }
+
+ /* Clean the list if there is an error */
+ if (ret)
+ tmc_clean_etr_buf_list(drvdata);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tmc_create_etr_buf_list);
+
int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
{
int ret = 0;
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 319a354ede9f..6e994678f926 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -208,6 +208,19 @@ struct tmc_resrv_buf {
s64 len;
};
+/**
+ * @sysfs_buf: Allocated sysfs_buf.
+ * @is_free: Indicates whether the buffer is free to choose.
+ * @pos: Offset to the start of the buffer.
+ * @link: list_head of the node.
+ */
+struct etr_buf_node {
+ struct etr_buf *sysfs_buf;
+ bool is_free;
+ loff_t pos;
+ struct list_head link;
+};
+
/**
* struct tmc_drvdata - specifics associated to an TMC component
* @atclk: optional clock for the core parts of the TMC.
@@ -245,6 +258,7 @@ struct tmc_resrv_buf {
* (after crash) by default.
* @crash_mdata: Reserved memory for storing tmc crash metadata.
* Used by ETR/ETF.
+ * @etr_buf_list: List that is used to manage allocated etr_buf.
*/
struct tmc_drvdata {
struct clk *atclk;
@@ -275,6 +289,7 @@ struct tmc_drvdata {
struct etr_buf *perf_buf;
struct tmc_resrv_buf resrv_buf;
struct tmc_resrv_buf crash_mdata;
+ struct list_head etr_buf_list;
};
struct etr_buf_operations {
@@ -447,5 +462,7 @@ struct etr_buf *tmc_etr_get_buffer(struct coresight_device *csdev,
enum cs_mode mode,
struct coresight_path *path);
extern const struct attribute_group coresight_etr_group;
+void tmc_clean_etr_buf_list(struct tmc_drvdata *drvdata);
+int tmc_create_etr_buf_list(struct tmc_drvdata *drvdata, int num_nodes);
#endif
--
2.34.1
^ permalink raw reply related
* [PATCH v17 0/7] coresight: ctcu: Enable byte-cntr function for TMC ETR
From: Jie Gan @ 2026-04-21 10:55 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
Bjorn Andersson, Konrad Dybcio
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
devicetree, Jie Gan, Konrad Dybcio, Mike Leach,
Krzysztof Kozlowski
The byte-cntr function provided by the CTCU device is used to count the
trace data entering the ETR. An interrupt is triggered if the data size
exceeds the threshold set in the BYTECNTRVAL register. The interrupt
handler counts the number of triggered interruptions.
Based on this concept, the irq_cnt can be used to determine whether
the etr_buf is full. The ETR device will be disabled when the active
etr_buf is nearly full or a timeout occurs. The nearly full buffer will
be switched to background after synced. A new buffer will be picked from
the etr_buf_list, then restart the ETR device.
The byte-cntr reading functions can access data from the synced and
deactivated buffer, transferring trace data from the etr_buf to userspace
without stopping the ETR device.
The byte-cntr read operation has integrated with the file node tmc_etr,
for example:
/dev/tmc_etr0
/dev/tmc_etr1
There are two scenarios for the tmc_etr file node with byte-cntr function:
1. BYTECNTRVAL register is configured and byte-cntr is enabled -> byte-cntr read
2. BYTECNTRVAL register is reset or byte-cntr is disabled -> original behavior
Shell commands to enable byte-cntr reading for etr0:
echo 1 > /sys/bus/coresight/devices/ctcu0/irq_enabled0
echo 1 > /sys/bus/coresight/devices/tmc_etr0/enable_sink
echo 1 > /sys/bus/coresight/devices/etm0/enable_source
cat /dev/tmc_etr0
Reset the BYTECNTR register for etr0:
echo 0 > /sys/bus/coresight/devices/ctcu0/irq_enabled0
Test Report:
=== Module setup ===
CONFIG_CORESIGHT=y (built-in, no action needed)
CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y (built-in, no action needed)
coresight-ctcu: not loaded, running modprobe...
coresight-ctcu: loaded
CTCU byte-cntr test
CTCU : ctcu0
ETR : tmc_etr0
source : etm0
chardev: /dev/tmc_etr0
module : coresight-ctcu
=== T1: device presence ===
PASS: CTCU device found: ctcu0
PASS: TMC ETR device found: tmc_etr0
=== T2: irq_enabled sysfs attributes ===
PASS: irq_enabled0 attribute exists
PASS: irq_enabled0 readable, value=0
PASS: irq_enabled1 attribute exists
PASS: irq_enabled1 readable, value=0
=== T3: irq_enabled write/read round-trip ===
PASS: irq_enabled0: write 1 -> read back 1
PASS: irq_enabled0: write 0 -> read back 0
PASS: irq_enabled1: write 1 -> read back 1
PASS: irq_enabled1: write 0 -> read back 0
=== T4: byte-cntr read with active trace ===
[step] cleanup: byte_cntr_disable
[step] enable_source = 0 (etm0)
[step] enable_sink = 0 (tmc_etr0)
[step] set irq_enabled0 = 0
[step] byte_cntr_disable done
[step] byte_cntr_enable
[step] set irq_enabled0 = 1
[step] set buffer_size = 0x2000000
[step] enable_sink = 1 (tmc_etr0)
[step] enable_source = 1 (etm0)
[step] byte_cntr_enable done
[step] cat /dev/tmc_etr0 > /tmp/tmc_etr0.bin &
[step] sleep 5 (accumulate trace data)
[step] byte_cntr_disable
[step] enable_source = 0 (etm0)
[step] enable_sink = 0 (tmc_etr0)
[step] set irq_enabled0 = 0
[step] byte_cntr_disable done
PASS: T4: cat exited naturally after source disabled (EOF delivered)
PASS: byte-cntr read returned 35333968 bytes -> /tmp/tmc_etr0.bin
PASS: no kernel warnings/oops after: byte-cntr read
=== T5: EBUSY on concurrent open while byte-cntr reading ===
[step] enable_source = 0 (etm0)
[step] enable_sink = 0 (tmc_etr0)
[step] set irq_enabled0 = 0
[step] byte_cntr_disable done
[step] set irq_enabled0 = 1
[step] set buffer_size = 0x2000000
[step] enable_sink = 1 (tmc_etr0)
[step] enable_source = 1 (etm0)
[step] byte_cntr_enable done
PASS: T5: second open correctly refused (EBUSY)
[step] enable_source = 0 (etm0)
[step] enable_sink = 0 (tmc_etr0)
[step] set irq_enabled0 = 0
[step] byte_cntr_disable done
PASS: no kernel warnings/oops after: concurrent open test
=== T6: rmmod while byte-cntr read is active ===
[step] enable_source = 0 (etm0)
[step] enable_sink = 0 (tmc_etr0)
[step] set irq_enabled0 = 0
[step] byte_cntr_disable done
[step] set irq_enabled0 = 1
[step] set buffer_size = 0x2000000
[step] enable_sink = 1 (tmc_etr0)
[step] enable_source = 1 (etm0)
[step] byte_cntr_enable done
PASS: T6: rmmod returned non-zero (device busy), no panic
PASS: no kernel warnings/oops after: rmmod while reading
[step] enable_source = 0 (etm0)
[step] enable_sink = 0 (tmc_etr0)
[step] set irq_enabled0 = 0
[step] byte_cntr_disable done
=== T7: insmod after rmmod and re-probe sanity ===
[step] module still loaded after T6, retrying rmmod
PASS: T7: modprobe coresight-ctcu succeeded
PASS: T7: CTCU device reappeared: ctcu0
PASS: no kernel warnings/oops after: insmod / re-probe
===================================
===================================
Results: PASS=20 FAIL=0 SKIP=0
---
Changes in v17:
1. fix race issue during allocat buffer.
2. fix user after free issue observed when remove module.
- Link to v16: https://lore.kernel.org/r/20260323-enable-byte-cntr-for-ctcu-v16-0-7a413d211b8d@oss.qualcomm.com
Changes in v16:
1. Remove lock/unlock processes in patch "coresight: tmc: add create/clean
functions for etr_buf_list" because we are allocating/freeing memory.
- Link to v15: https://lore.kernel.org/r/20260313-enable-byte-cntr-for-ctcu-v15-0-1777f14ed319@oss.qualcomm.com
Changes in v15:
1. add lockdep_assert_held in patch "coresight: tmc: add create/clean
functions for etr_buf_list"
2. optimize tmc_clean_etr_buf_list function
3. optimize the patch "enable byte-cntr for TMC ETR devices" according
to Suzuki's comments
- call byte_cntr_sysfs_ops from etr_sysfs_ops
- optimize the lock usage in all functions
- remove the buf_node parameter in etr_drvdata, move it to
byte_cntr_data
- move the tmc_reset_sysfs_buf function to tmc-etr.c
- add a read flag to struct etr_buf_node to allow updating pos while
traversing etr_buf_list during data reads.
Link to v14: https://lore.kernel.org/r/20260309-enable-byte-cntr-for-ctcu-v14-0-c08823e5a8e6@oss.qualcomm.com
Changes in V14:
1. Drop the patch: integrate byte-cntr's sysfs_ops with tmc sysfs file_ops
2. Replace tmc_sysfs_ops with byte_cntr_sysfs_ops in byte_cntr_start
function and restore etr_sysfs_ops in byte_cntr_unprepare function.
3. Remove redundant checks in byte‑cntr functions.
Link to V13: https://lore.kernel.org/all/20260223-enable-byte-cntr-for-ctcu-v13-0-9cb44178b250@oss.qualcomm.com/
Changes in v13:
1. initilize the byte_cntr_data->raw_spin_lock before using.
2. replace kzalloc with kzalloc_obj.
Link to V12: https://lore.kernel.org/all/20260203-enable-byte-cntr-for-ctcu-v12-0-7bf81b86b70e@oss.qualcomm.com/
Changes in v12:
1. Add a new function for retrieving the CTCU's coresight_dev instead of
refactor the existing function.
Link to v11: https://lore.kernel.org/r/20260126-enable-byte-cntr-for-ctcu-v11-0-c0af66ba15cf@oss.qualcomm.com
Changes in v11:
1. Correct the description in patch1 for the function coresight_get_in_port.
2. Renaming the sysfs_ops to tmc_sysfs_ops per Suzuki's suggestion.
Link to v10: https://lore.kernel.org/r/20260122-enable-byte-cntr-for-ctcu-v10-0-22978e3c169f@oss.qualcomm.com
Changes in v10:
1. fix a free memory issue that is reported by robot for patch 2.
Link to v9: https://lore.kernel.org/r/20251224-enable-byte-cntr-for-ctcu-v9-0-886c4496fed4@oss.qualcomm.com
Changes in v9:
1. Drop the patch: add a new API to retrieve the helper device
2. Add a new patch to refactor the tmc_etr_get_catu_device function,
making it generic to support all types of helper devices associated with ETR.
3. Optimizing the code for creating irq_threshold sysfs node.
4. Remove interrupt-name property and obtain the IRQ based on the
in-port number.
Link to v8: https://lore.kernel.org/r/20251211-enable-byte-cntr-for-ctcu-v8-0-3e12ff313191@oss.qualcomm.com
Changes in V8:
1. Optimizing the patch 1 and patch 2 according to Suzuki's comments.
2. Combine the patch 3 and patch 4 together.
3. Rename the interrupt-name to prevent confusion, for example:etr0->etrirq0.
Link to V7 - https://lore.kernel.org/all/20251013-enable-byte-cntr-for-ctcu-v7-0-e1e8f41e15dd@oss.qualcomm.com/
Changes in V7:
1. rebased on tag next-20251010
2. updated info for sysfs node document
Link to V6 - https://lore.kernel.org/all/20250908-enable-byte-cntr-for-tmc-v6-0-1db9e621441a@oss.qualcomm.com/
Changes in V6:
1. rebased on next-20250905.
2. fixed the issue that the dtsi file has re-named from sa8775p.dtsi to
lemans.dtsi.
3. fixed some minor issues about comments.
Link to V5 - https://lore.kernel.org/all/20250812083731.549-1-jie.gan@oss.qualcomm.com/
Changes in V5:
1. Add Mike's reviewed-by tag for patchset 1,2,5.
2. Remove the function pointer added to helper_ops according to Mike's
comment, it also results the patchset has been removed.
3. Optimizing the paired create/clean functions for etr_buf_list.
4. Remove the unneeded parameter "reading" from the etr_buf_node.
Link to V4 - https://lore.kernel.org/all/20250725100806.1157-1-jie.gan@oss.qualcomm.com/
Changes in V4:
1. Rename the function to coresight_get_in_port_dest regarding to Mike's
comment (patch 1/10).
2. Add lock to protect the connections regarding to Mike's comment
(patch 2/10).
3. Move all byte-cntr functions to coresight-ctcu-byte-cntr file.
4. Add tmc_read_ops to wrap all read operations for TMC device.
5. Add a function in helper_ops to check whether the byte-cntr is
enabkled.
6. Call byte-cntr's read_ops if byte-cntr is enabled when reading data
from the sysfs node.
Link to V3 resend - https://lore.kernel.org/all/20250714063109.591-1-jie.gan@oss.qualcomm.com/
Changes in V3 resend:
1. rebased on next-20250711.
Link to V3 - https://lore.kernel.org/all/20250624060438.7469-1-jie.gan@oss.qualcomm.com/
Changes in V3:
1. The previous solution has been deprecated.
2. Add a etr_buf_list to manage allcated etr buffers.
3. Add a logic to switch buffer for ETR.
4. Add read functions to read trace data from synced etr buffer.
Link to V2 - https://lore.kernel.org/all/20250410013330.3609482-1-jie.gan@oss.qualcomm.com/
Changes in V2:
1. Removed the independent file node /dev/byte_cntr.
2. Integrated the byte-cntr's file operations with current ETR file
node.
3. Optimized the driver code of the CTCU that associated with byte-cntr.
4. Add kernel document for the export API tmc_etr_get_rwp_offset.
5. Optimized the way to read the rwp_offset according to Mike's
suggestion.
6. Removed the dependency of the dts patch.
Link to V1 - https://lore.kernel.org/all/20250310090407.2069489-1-quic_jiegan@quicinc.com/
To: Suzuki K Poulose <suzuki.poulose@arm.com>
To: Mike Leach <mike.leach@arm.com>
To: James Clark <james.clark@linaro.org>
To: Alexander Shishkin <alexander.shishkin@linux.intel.com>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Tingwei Zhang <tingwei.zhang@oss.qualcomm.com>
To: Bjorn Andersson <andersson@kernel.org>
To: Konrad Dybcio <konradybcio@kernel.org>
Cc: coresight@lists.linaro.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-msm@vger.kernel.org
Cc: devicetree@vger.kernel.org
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
Jie Gan (7):
coresight: core: refactor ctcu_get_active_port and make it generic
coresight: tmc: add create/clean functions for etr_buf_list
coresight: tmc: introduce tmc_sysfs_ops to wrap sysfs read operations
coresight: etr: add a new function to retrieve the CTCU device
dt-bindings: arm: add an interrupt property for Coresight CTCU
coresight: ctcu: enable byte-cntr for TMC ETR devices
arm64: dts: qcom: lemans: add interrupts to CTCU device
.../ABI/testing/sysfs-bus-coresight-devices-ctcu | 9 +
.../bindings/arm/qcom,coresight-ctcu.yaml | 10 +
arch/arm64/boot/dts/qcom/lemans.dtsi | 3 +
drivers/hwtracing/coresight/Makefile | 2 +-
drivers/hwtracing/coresight/coresight-core.c | 24 ++
.../hwtracing/coresight/coresight-ctcu-byte-cntr.c | 298 +++++++++++++++++++++
drivers/hwtracing/coresight/coresight-ctcu-core.c | 143 ++++++++--
drivers/hwtracing/coresight/coresight-ctcu.h | 79 +++++-
drivers/hwtracing/coresight/coresight-priv.h | 2 +
drivers/hwtracing/coresight/coresight-tmc-core.c | 55 ++--
drivers/hwtracing/coresight/coresight-tmc-etr.c | 237 +++++++++++++++-
drivers/hwtracing/coresight/coresight-tmc.h | 42 +++
12 files changed, 832 insertions(+), 72 deletions(-)
---
base-commit: 936c21068d7ade00325e40d82bfd2f3f29d9f659
change-id: 20260309-enable-byte-cntr-for-ctcu-ff86e6198b7f
Best regards,
--
Jie Gan <jie.gan@oss.qualcomm.com>
^ permalink raw reply
* [PATCH v17 3/7] coresight: tmc: introduce tmc_sysfs_ops to wrap sysfs read operations
From: Jie Gan @ 2026-04-21 10:55 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
Bjorn Andersson, Konrad Dybcio
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
devicetree, Jie Gan, Mike Leach
In-Reply-To: <20260421-enable-byte-cntr-for-ctcu-v17-0-9cf36ff55fc0@oss.qualcomm.com>
Introduce tmc_sysfs_ops as a wrapper, wrap sysfs read operations,
for reading trace data from the TMC buffer.
Reviewed-by: Mike Leach <mike.leach@linaro.org>
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-tmc-core.c | 51 ++++++++++--------------
drivers/hwtracing/coresight/coresight-tmc.h | 15 +++++++
2 files changed, 37 insertions(+), 29 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
index bac3278ef4dd..110eedde077f 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-core.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
@@ -228,17 +228,10 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata)
{
int ret = 0;
- switch (drvdata->config_type) {
- case TMC_CONFIG_TYPE_ETB:
- case TMC_CONFIG_TYPE_ETF:
- ret = tmc_read_prepare_etb(drvdata);
- break;
- case TMC_CONFIG_TYPE_ETR:
- ret = tmc_read_prepare_etr(drvdata);
- break;
- default:
+ if (drvdata->sysfs_ops)
+ ret = drvdata->sysfs_ops->read_prepare(drvdata);
+ else
ret = -EINVAL;
- }
if (!ret)
dev_dbg(&drvdata->csdev->dev, "TMC read start\n");
@@ -250,17 +243,10 @@ static int tmc_read_unprepare(struct tmc_drvdata *drvdata)
{
int ret = 0;
- switch (drvdata->config_type) {
- case TMC_CONFIG_TYPE_ETB:
- case TMC_CONFIG_TYPE_ETF:
- ret = tmc_read_unprepare_etb(drvdata);
- break;
- case TMC_CONFIG_TYPE_ETR:
- ret = tmc_read_unprepare_etr(drvdata);
- break;
- default:
+ if (drvdata->sysfs_ops)
+ ret = drvdata->sysfs_ops->read_unprepare(drvdata);
+ else
ret = -EINVAL;
- }
if (!ret)
dev_dbg(&drvdata->csdev->dev, "TMC read end\n");
@@ -287,15 +273,7 @@ static int tmc_open(struct inode *inode, struct file *file)
static ssize_t tmc_get_sysfs_trace(struct tmc_drvdata *drvdata, loff_t pos, size_t len,
char **bufpp)
{
- switch (drvdata->config_type) {
- case TMC_CONFIG_TYPE_ETB:
- case TMC_CONFIG_TYPE_ETF:
- return tmc_etb_get_sysfs_trace(drvdata, pos, len, bufpp);
- case TMC_CONFIG_TYPE_ETR:
- return tmc_etr_get_sysfs_trace(drvdata, pos, len, bufpp);
- }
-
- return -EINVAL;
+ return drvdata->sysfs_ops->get_trace_data(drvdata, pos, len, bufpp);
}
static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
@@ -764,6 +742,18 @@ static void register_crash_dev_interface(struct tmc_drvdata *drvdata,
"Valid crash tracedata found\n");
}
+static const struct tmc_sysfs_ops etb_sysfs_ops = {
+ .read_prepare = tmc_read_prepare_etb,
+ .read_unprepare = tmc_read_unprepare_etb,
+ .get_trace_data = tmc_etb_get_sysfs_trace,
+};
+
+static const struct tmc_sysfs_ops etr_sysfs_ops = {
+ .read_prepare = tmc_read_prepare_etr,
+ .read_unprepare = tmc_read_unprepare_etr,
+ .get_trace_data = tmc_etr_get_sysfs_trace,
+};
+
static int __tmc_probe(struct device *dev, struct resource *res)
{
int ret = 0;
@@ -823,6 +813,7 @@ static int __tmc_probe(struct device *dev, struct resource *res)
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc.ops = &tmc_etb_cs_ops;
dev_list = "tmc_etb";
+ drvdata->sysfs_ops = &etb_sysfs_ops;
break;
case TMC_CONFIG_TYPE_ETR:
desc.groups = coresight_etr_groups;
@@ -835,6 +826,7 @@ static int __tmc_probe(struct device *dev, struct resource *res)
idr_init(&drvdata->idr);
mutex_init(&drvdata->idr_mutex);
dev_list = "tmc_etr";
+ drvdata->sysfs_ops = &etr_sysfs_ops;
INIT_LIST_HEAD(&drvdata->etr_buf_list);
break;
case TMC_CONFIG_TYPE_ETF:
@@ -844,6 +836,7 @@ static int __tmc_probe(struct device *dev, struct resource *res)
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
desc.ops = &tmc_etf_cs_ops;
dev_list = "tmc_etf";
+ drvdata->sysfs_ops = &etb_sysfs_ops;
break;
default:
pr_err("%s: Unsupported TMC config\n", desc.name);
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 6e994678f926..a14645b04624 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -259,6 +259,7 @@ struct etr_buf_node {
* @crash_mdata: Reserved memory for storing tmc crash metadata.
* Used by ETR/ETF.
* @etr_buf_list: List that is used to manage allocated etr_buf.
+ * @sysfs_ops: Read operations for the sysfs mode.
*/
struct tmc_drvdata {
struct clk *atclk;
@@ -290,6 +291,20 @@ struct tmc_drvdata {
struct tmc_resrv_buf resrv_buf;
struct tmc_resrv_buf crash_mdata;
struct list_head etr_buf_list;
+ const struct tmc_sysfs_ops *sysfs_ops;
+};
+
+/**
+ * struct tmc_sysfs_ops - read operations for TMC and its helper devices
+ * @read_prepare: prepare operation.
+ * @read_unprepare: unprepare operation.
+ * @get_trace_data: read operation.
+ */
+struct tmc_sysfs_ops {
+ int (*read_prepare)(struct tmc_drvdata *drvdata);
+ int (*read_unprepare)(struct tmc_drvdata *drvdata);
+ ssize_t (*get_trace_data)(struct tmc_drvdata *drvdata, loff_t pos,
+ size_t len, char **bufpp);
};
struct etr_buf_operations {
--
2.34.1
^ permalink raw reply related
* [PATCH v17 4/7] coresight: etr: add a new function to retrieve the CTCU device
From: Jie Gan @ 2026-04-21 10:55 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
Bjorn Andersson, Konrad Dybcio
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
devicetree, Jie Gan
In-Reply-To: <20260421-enable-byte-cntr-for-ctcu-v17-0-9cf36ff55fc0@oss.qualcomm.com>
Add tmc_etr_get_ctcu_device function to find the ptr of the
coresight_device of the CTCU device if the CTCU device is connected to
the TMC ETR device.
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-tmc-etr.c | 24 ++++++++++++++++++++++++
drivers/hwtracing/coresight/coresight-tmc.h | 1 +
2 files changed, 25 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index ac704617097c..bb76e7e37874 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -865,6 +865,30 @@ tmc_etr_get_catu_device(struct tmc_drvdata *drvdata)
}
EXPORT_SYMBOL_GPL(tmc_etr_get_catu_device);
+/*
+ * TMC ETR could be connected to a CTCU device, which can provide ATID filter
+ * and byte-cntr service. This is represented by the output port of the TMC
+ * (ETR) connected to the input port of the CTCU.
+ *
+ * Returns : coresight_device ptr for the CTCU device if a CTCU is found.
+ * : NULL otherwise.
+ */
+struct coresight_device *
+tmc_etr_get_ctcu_device(struct tmc_drvdata *drvdata)
+{
+ struct coresight_device *etr = drvdata->csdev;
+ union coresight_dev_subtype ctcu_subtype = {
+ .helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_CTCU
+ };
+
+ if (!IS_ENABLED(CONFIG_CORESIGHT_CTCU))
+ return NULL;
+
+ return coresight_find_output_type(etr->pdata, CORESIGHT_DEV_TYPE_HELPER,
+ ctcu_subtype);
+}
+EXPORT_SYMBOL_GPL(tmc_etr_get_ctcu_device);
+
static const struct etr_buf_operations *etr_buf_ops[] = {
[ETR_MODE_FLAT] = &etr_flat_buf_ops,
[ETR_MODE_ETR_SG] = &etr_sg_buf_ops,
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index a14645b04624..fbb015079872 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -470,6 +470,7 @@ static inline uint32_t find_crash_tracedata_crc(struct tmc_drvdata *drvdata,
}
struct coresight_device *tmc_etr_get_catu_device(struct tmc_drvdata *drvdata);
+struct coresight_device *tmc_etr_get_ctcu_device(struct tmc_drvdata *drvdata);
void tmc_etr_set_catu_ops(const struct etr_buf_operations *catu);
void tmc_etr_remove_catu_ops(void);
--
2.34.1
^ permalink raw reply related
* [PATCH v17 5/7] dt-bindings: arm: add an interrupt property for Coresight CTCU
From: Jie Gan @ 2026-04-21 10:55 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
Bjorn Andersson, Konrad Dybcio
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
devicetree, Jie Gan, Krzysztof Kozlowski, Mike Leach
In-Reply-To: <20260421-enable-byte-cntr-for-ctcu-v17-0-9cf36ff55fc0@oss.qualcomm.com>
Add an interrupt property to CTCU device. The interrupt will be triggered
when the data size in the ETR buffer exceeds the threshold of the
BYTECNTRVAL register. Programming a threshold in the BYTECNTRVAL register
of CTCU device will enable the interrupt.
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Reviewed-by: Mike Leach <mike.leach@linaro.org>
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
Documentation/devicetree/bindings/arm/qcom,coresight-ctcu.yaml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/qcom,coresight-ctcu.yaml b/Documentation/devicetree/bindings/arm/qcom,coresight-ctcu.yaml
index e002f87361ad..2981001a7d7f 100644
--- a/Documentation/devicetree/bindings/arm/qcom,coresight-ctcu.yaml
+++ b/Documentation/devicetree/bindings/arm/qcom,coresight-ctcu.yaml
@@ -44,6 +44,11 @@ properties:
items:
- const: apb
+ interrupts:
+ items:
+ - description: Interrupt for the ETR device connected to in-port0.
+ - description: Interrupt for the ETR device connected to in-port1.
+
label:
description:
Description of a coresight device.
@@ -65,6 +70,8 @@ additionalProperties: false
examples:
- |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
ctcu@1001000 {
compatible = "qcom,sa8775p-ctcu";
reg = <0x1001000 0x1000>;
@@ -72,6 +79,9 @@ examples:
clocks = <&aoss_qmp>;
clock-names = "apb";
+ interrupts = <GIC_SPI 270 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 262 IRQ_TYPE_EDGE_RISING>;
+
in-ports {
#address-cells = <1>;
#size-cells = <0>;
--
2.34.1
^ permalink raw reply related
* [PATCH v17 6/7] coresight: ctcu: enable byte-cntr for TMC ETR devices
From: Jie Gan @ 2026-04-21 10:55 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
Bjorn Andersson, Konrad Dybcio
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
devicetree, Jie Gan
In-Reply-To: <20260421-enable-byte-cntr-for-ctcu-v17-0-9cf36ff55fc0@oss.qualcomm.com>
The byte-cntr function provided by the CTCU device is used to transfer data
from the ETR buffer to the userspace. An interrupt is triggered if the data
size exceeds the threshold set in the BYTECNTRVAL register. The interrupt
handler counts the number of triggered interruptions and the read function
will read the data from the synced ETR buffer.
Switching the sysfs_buf when current buffer is full or the timeout is
triggered and resets rrp and rwp registers after switched the buffer.
The synced buffer will become available for reading after the switch.
Byte-cntr workflow:
start -> ctcu_enable(ctcu_byte_cntr_start) -> tmc_enable_etr_sink ->
tmc_read_prepare_etr(jump to tmc_read_prepare_byte_cntr) ->
tmc_etr_get_sysfs_trace(jump to tmc_byte_cntr_get_data) ->
tmc_disable_etr_sink -> ctcu_disable(ctcu_byte_cntr_stop) ->
tmc_read_unprepare_etr(jump to tmc_read_unprepare_byte_cntr) -> finish
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
.../ABI/testing/sysfs-bus-coresight-devices-ctcu | 9 +
drivers/hwtracing/coresight/Makefile | 2 +-
.../hwtracing/coresight/coresight-ctcu-byte-cntr.c | 298 +++++++++++++++++++++
drivers/hwtracing/coresight/coresight-ctcu-core.c | 124 ++++++++-
drivers/hwtracing/coresight/coresight-ctcu.h | 79 +++++-
drivers/hwtracing/coresight/coresight-tmc-core.c | 3 +-
drivers/hwtracing/coresight/coresight-tmc-etr.c | 114 +++++++-
drivers/hwtracing/coresight/coresight-tmc.h | 9 +
8 files changed, 613 insertions(+), 25 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu
new file mode 100644
index 000000000000..3b400480ad53
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu
@@ -0,0 +1,9 @@
+What: /sys/bus/coresight/devices/<ctcu-name>/irq_enabled[0:1]
+Date: March 2026
+KernelVersion: 7.2
+Contact: Tingwei Zhang <tingwei.zhang@oss.qualcomm.com>; Jinlong Mao <jinlong.mao@oss.qualcomm.com>; Jie Gan <jie.gan@oss.qualcomm.com>
+Description:
+ (RW) Configure the flag to enable interrupt to count data during CTCU enablement.
+ An interrupt is generated when the data size exceeds the value set in the IRQ register.
+ 0 : disable
+ 1 : enable
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index ab16d06783a5..821a1b06b20c 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -55,5 +55,5 @@ coresight-cti-y := coresight-cti-core.o coresight-cti-platform.o \
obj-$(CONFIG_ULTRASOC_SMB) += ultrasoc-smb.o
obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
obj-$(CONFIG_CORESIGHT_CTCU) += coresight-ctcu.o
-coresight-ctcu-y := coresight-ctcu-core.o
+coresight-ctcu-y := coresight-ctcu-core.o coresight-ctcu-byte-cntr.o
obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) += coresight-kunit-tests.o
diff --git a/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c b/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c
new file mode 100644
index 000000000000..2e136aa4f219
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/coresight.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/of_irq.h>
+#include <linux/uaccess.h>
+
+#include "coresight-ctcu.h"
+#include "coresight-priv.h"
+#include "coresight-tmc.h"
+
+static irqreturn_t byte_cntr_handler(int irq, void *data)
+{
+ struct ctcu_byte_cntr *byte_cntr_data = data;
+
+ atomic_inc(&byte_cntr_data->irq_cnt);
+ wake_up(&byte_cntr_data->wq);
+
+ return IRQ_HANDLED;
+}
+
+static void ctcu_cfg_byte_cntr_reg(struct ctcu_drvdata *drvdata, u32 val,
+ u32 offset)
+{
+ /* A one value for IRQCTRL register represents 8 bytes */
+ ctcu_program_register(drvdata, val / 8, offset);
+}
+
+static struct ctcu_byte_cntr *ctcu_get_byte_cntr(struct coresight_device *ctcu,
+ struct coresight_device *etr)
+{
+ struct ctcu_drvdata *drvdata = dev_get_drvdata(ctcu->dev.parent);
+ int port;
+
+ port = coresight_get_in_port(etr, ctcu);
+ if (port < 0 || port > 1)
+ return NULL;
+
+ return &drvdata->byte_cntr_data[port];
+}
+
+static bool ctcu_byte_cntr_switch_buffer(struct tmc_drvdata *etr_drvdata,
+ struct ctcu_byte_cntr *byte_cntr_data)
+{
+ struct etr_buf_node *nd, *next, *curr_node = NULL, *picked_node = NULL;
+ struct etr_buf *curr_buf = etr_drvdata->sysfs_buf;
+ bool found_free_buf = false;
+
+ if (WARN_ON(!etr_drvdata || !byte_cntr_data))
+ return false;
+
+ /* Stop the ETR before initiating the switch */
+ if (coresight_get_mode(etr_drvdata->csdev) != CS_MODE_DISABLED)
+ tmc_etr_enable_disable_hw(etr_drvdata, false);
+
+ list_for_each_entry_safe(nd, next, &etr_drvdata->etr_buf_list, link) {
+ /* curr_buf is free for next round */
+ if (nd->sysfs_buf == curr_buf) {
+ nd->is_free = true;
+ curr_node = nd;
+ } else if (!found_free_buf && nd->is_free) {
+ picked_node = nd;
+ found_free_buf = true;
+ }
+ }
+
+ if (found_free_buf) {
+ curr_node->pos = 0;
+ curr_node->reading = true;
+ byte_cntr_data->buf_node = curr_node;
+ etr_drvdata->sysfs_buf = picked_node->sysfs_buf;
+ etr_drvdata->etr_buf = picked_node->sysfs_buf;
+ picked_node->is_free = false;
+ /* Reset irq_cnt for next etr_buf */
+ atomic_set(&byte_cntr_data->irq_cnt, 0);
+ /* Restart the ETR once a free buffer is available */
+ if (coresight_get_mode(etr_drvdata->csdev) != CS_MODE_DISABLED)
+ tmc_etr_enable_disable_hw(etr_drvdata, true);
+ }
+
+ return found_free_buf;
+}
+
+/*
+ * ctcu_byte_cntr_get_data() - reads data from the deactivated and filled buffer.
+ * The byte-cntr reading work reads data from the deactivated and filled buffer.
+ * The read operation waits for a buffer to become available, either filled or
+ * upon timeout, and then reads trace data from the synced buffer.
+ */
+static ssize_t tmc_byte_cntr_get_data(struct tmc_drvdata *etr_drvdata, loff_t pos,
+ size_t len, char **bufpp)
+{
+ struct coresight_device *ctcu = tmc_etr_get_ctcu_device(etr_drvdata);
+ struct device *dev = &etr_drvdata->csdev->dev;
+ struct ctcu_byte_cntr *byte_cntr_data;
+ struct etr_buf *sysfs_buf;
+ atomic_t *irq_cnt;
+ ssize_t actual;
+ int ret;
+
+ byte_cntr_data = ctcu_get_byte_cntr(ctcu, etr_drvdata->csdev);
+ if (!byte_cntr_data || !byte_cntr_data->irq_enabled)
+ return -EINVAL;
+
+ irq_cnt = &byte_cntr_data->irq_cnt;
+
+wait_buffer:
+ if (!byte_cntr_data->buf_node) {
+ ret = wait_event_interruptible_timeout(byte_cntr_data->wq,
+ (atomic_read(irq_cnt) >= MAX_IRQ_CNT - 1) ||
+ !byte_cntr_data->enable,
+ BYTE_CNTR_TIMEOUT);
+ if (ret < 0)
+ return ret;
+ /*
+ * The current etr_buf is almost full or timeout is triggered,
+ * so switch the buffer and mark the switched buffer as reading.
+ */
+ if (byte_cntr_data->enable) {
+ if (!ctcu_byte_cntr_switch_buffer(etr_drvdata, byte_cntr_data)) {
+ dev_err(dev, "Switch buffer failed for the byte-cntr\n");
+ return -ENOMEM;
+ }
+ } else {
+ /* Exit byte-cntr reading */
+ return 0;
+ }
+ }
+
+ /* Check the status of current etr_buf */
+ if (atomic_read(irq_cnt) >= MAX_IRQ_CNT)
+ dev_warn(dev, "Data overwrite happened\n");
+
+ pos = byte_cntr_data->buf_node->pos;
+ sysfs_buf = byte_cntr_data->buf_node->sysfs_buf;
+ actual = tmc_etr_read_sysfs_buf(sysfs_buf, pos, len, bufpp);
+ if (actual <= 0) {
+ /* Reset buf_node upon reading is finished or failed */
+ byte_cntr_data->buf_node->reading = false;
+ byte_cntr_data->buf_node = NULL;
+
+ /*
+ * Nothing in the buffer, waiting for the next buffer
+ * to be filled.
+ */
+ if (actual == 0)
+ goto wait_buffer;
+ }
+
+ return actual;
+}
+
+static int tmc_read_prepare_byte_cntr(struct tmc_drvdata *etr_drvdata)
+{
+ struct coresight_device *ctcu = tmc_etr_get_ctcu_device(etr_drvdata);
+ struct ctcu_byte_cntr *byte_cntr_data;
+ unsigned long flags;
+ int ret = 0;
+
+ /* byte-cntr is operating with SYSFS mode being enabled only */
+ if (coresight_get_mode(etr_drvdata->csdev) != CS_MODE_SYSFS)
+ return -EINVAL;
+
+ byte_cntr_data = ctcu_get_byte_cntr(ctcu, etr_drvdata->csdev);
+ if (!byte_cntr_data || !byte_cntr_data->irq_enabled)
+ return -EINVAL;
+
+ raw_spin_lock_irqsave(&byte_cntr_data->spin_lock, flags);
+ if (byte_cntr_data->reading) {
+ raw_spin_unlock_irqrestore(&byte_cntr_data->spin_lock, flags);
+ return -EBUSY;
+ }
+
+ byte_cntr_data->reading = true;
+ raw_spin_unlock_irqrestore(&byte_cntr_data->spin_lock, flags);
+ /* Setup an available etr_buf_list for byte-cntr */
+ ret = tmc_create_etr_buf_list(etr_drvdata, 2);
+ if (ret) {
+ byte_cntr_data->reading = false;
+ return ret;
+ }
+
+ guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
+ atomic_set(&byte_cntr_data->irq_cnt, 0);
+ /*
+ * Configure the byte-cntr register to enable IRQ. The configured
+ * size is 5% of the buffer_size.
+ */
+ ctcu_cfg_byte_cntr_reg(byte_cntr_data->ctcu_drvdata,
+ etr_drvdata->size / MAX_IRQ_CNT,
+ byte_cntr_data->irq_ctrl_offset);
+ enable_irq_wake(byte_cntr_data->irq);
+ byte_cntr_data->buf_node = NULL;
+
+ return 0;
+}
+
+static int tmc_read_unprepare_byte_cntr(struct tmc_drvdata *etr_drvdata)
+{
+ struct coresight_device *ctcu = tmc_etr_get_ctcu_device(etr_drvdata);
+ struct ctcu_byte_cntr *byte_cntr_data;
+
+ byte_cntr_data = ctcu_get_byte_cntr(ctcu, etr_drvdata->csdev);
+ if (!byte_cntr_data || !byte_cntr_data->irq_enabled)
+ return -EINVAL;
+
+ tmc_clean_etr_buf_list(etr_drvdata);
+ scoped_guard(raw_spinlock_irqsave, &byte_cntr_data->spin_lock) {
+ /* Configure the byte-cntr register to disable IRQ */
+ ctcu_cfg_byte_cntr_reg(byte_cntr_data->ctcu_drvdata, 0,
+ byte_cntr_data->irq_ctrl_offset);
+ disable_irq_wake(byte_cntr_data->irq);
+ byte_cntr_data->buf_node = NULL;
+ byte_cntr_data->reading = false;
+ }
+ wake_up(&byte_cntr_data->wq);
+
+ return 0;
+}
+
+const struct tmc_sysfs_ops byte_cntr_sysfs_ops = {
+ .read_prepare = tmc_read_prepare_byte_cntr,
+ .read_unprepare = tmc_read_unprepare_byte_cntr,
+ .get_trace_data = tmc_byte_cntr_get_data,
+};
+
+/* Start the byte-cntr function when the path is enabled. */
+void ctcu_byte_cntr_start(struct coresight_device *csdev, struct coresight_path *path)
+{
+ struct coresight_device *sink = coresight_get_sink(path);
+ struct ctcu_byte_cntr *byte_cntr_data;
+
+ byte_cntr_data = ctcu_get_byte_cntr(csdev, sink);
+ if (!byte_cntr_data)
+ return;
+
+ /* Don't start byte-cntr function when irq_enabled is not set. */
+ if (!byte_cntr_data->irq_enabled || byte_cntr_data->enable)
+ return;
+
+ guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
+ byte_cntr_data->enable = true;
+}
+
+/* Stop the byte-cntr function when the path is disabled. */
+void ctcu_byte_cntr_stop(struct coresight_device *csdev, struct coresight_path *path)
+{
+ struct coresight_device *sink = coresight_get_sink(path);
+ struct ctcu_byte_cntr *byte_cntr_data;
+
+ if (coresight_get_mode(sink) == CS_MODE_SYSFS)
+ return;
+
+ byte_cntr_data = ctcu_get_byte_cntr(csdev, sink);
+ if (!byte_cntr_data)
+ return;
+
+ guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
+ byte_cntr_data->enable = false;
+}
+
+void ctcu_byte_cntr_init(struct device *dev, struct ctcu_drvdata *drvdata, int etr_num)
+{
+ struct ctcu_byte_cntr *byte_cntr_data;
+ struct device_node *nd = dev->of_node;
+ int irq_num, ret, i, irq_registered = 0;
+
+ for (i = 0; i < etr_num; i++) {
+ byte_cntr_data = &drvdata->byte_cntr_data[i];
+ irq_num = of_irq_get(nd, i);
+ if (irq_num < 0) {
+ dev_err(dev, "Failed to get IRQ from DT for port%d\n", i);
+ continue;
+ }
+
+ ret = devm_request_irq(dev, irq_num, byte_cntr_handler,
+ IRQF_TRIGGER_RISING | IRQF_SHARED,
+ dev_name(dev), byte_cntr_data);
+ if (ret) {
+ dev_err(dev, "Failed to register IRQ for port%d\n", i);
+ continue;
+ }
+
+ byte_cntr_data->irq = irq_num;
+ byte_cntr_data->ctcu_drvdata = drvdata;
+ init_waitqueue_head(&byte_cntr_data->wq);
+ raw_spin_lock_init(&byte_cntr_data->spin_lock);
+ irq_registered++;
+ }
+
+ if (irq_registered)
+ tmc_etr_set_byte_cntr_sysfs_ops(&byte_cntr_sysfs_ops);
+}
diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c
index e8720026c9e3..897d51936b88 100644
--- a/drivers/hwtracing/coresight/coresight-ctcu-core.c
+++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2024-2026 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/clk.h>
@@ -18,6 +19,7 @@
#include "coresight-ctcu.h"
#include "coresight-priv.h"
+#include "coresight-tmc.h"
#define ctcu_writel(drvdata, val, offset) __raw_writel((val), drvdata->base + offset)
#define ctcu_readl(drvdata, offset) __raw_readl(drvdata->base + offset)
@@ -43,17 +45,21 @@
#define CTCU_ATID_REG_BIT(traceid) (traceid % 32)
#define CTCU_ATID_REG_SIZE 0x10
+#define CTCU_ETR0_IRQCTRL 0x6c
+#define CTCU_ETR1_IRQCTRL 0x70
#define CTCU_ETR0_ATID0 0xf8
#define CTCU_ETR1_ATID0 0x108
static const struct ctcu_etr_config sa8775p_etr_cfgs[] = {
{
- .atid_offset = CTCU_ETR0_ATID0,
- .port_num = 0,
+ .atid_offset = CTCU_ETR0_ATID0,
+ .irq_ctrl_offset = CTCU_ETR0_IRQCTRL,
+ .port_num = 0,
},
{
- .atid_offset = CTCU_ETR1_ATID0,
- .port_num = 1,
+ .atid_offset = CTCU_ETR1_ATID0,
+ .irq_ctrl_offset = CTCU_ETR1_IRQCTRL,
+ .port_num = 1,
},
};
@@ -62,6 +68,85 @@ static const struct ctcu_config sa8775p_cfgs = {
.num_etr_config = ARRAY_SIZE(sa8775p_etr_cfgs),
};
+void ctcu_program_register(struct ctcu_drvdata *drvdata, u32 val, u32 offset)
+{
+ CS_UNLOCK(drvdata->base);
+ ctcu_writel(drvdata, val, offset);
+ CS_LOCK(drvdata->base);
+}
+
+static ssize_t irq_enabled_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ctcu_byte_cntr_irq_attribute *irq_attr =
+ container_of(attr, struct ctcu_byte_cntr_irq_attribute, attr);
+ struct ctcu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ u8 port = irq_attr->port;
+
+ if (!drvdata->byte_cntr_data[port].irq_ctrl_offset)
+ return -EINVAL;
+
+ return sysfs_emit(buf, "%u\n",
+ (unsigned int)drvdata->byte_cntr_data[port].irq_enabled);
+}
+
+static ssize_t irq_enabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct ctcu_byte_cntr_irq_attribute *irq_attr =
+ container_of(attr, struct ctcu_byte_cntr_irq_attribute, attr);
+ struct ctcu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ u8 port = irq_attr->port;
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ guard(raw_spinlock_irqsave)(&drvdata->byte_cntr_data[port].spin_lock);
+ if (drvdata->byte_cntr_data[port].reading)
+ return -EBUSY;
+ else if (drvdata->byte_cntr_data[port].irq_ctrl_offset)
+ drvdata->byte_cntr_data[port].irq_enabled = !!val;
+
+ return size;
+}
+
+static umode_t irq_enabled_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device_attribute *dev_attr =
+ container_of(attr, struct device_attribute, attr);
+ struct ctcu_byte_cntr_irq_attribute *irq_attr =
+ container_of(dev_attr, struct ctcu_byte_cntr_irq_attribute, attr);
+ struct device *dev = kobj_to_dev(kobj);
+ struct ctcu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ u8 port = irq_attr->port;
+
+ if (drvdata && drvdata->byte_cntr_data[port].irq_ctrl_offset)
+ return attr->mode;
+
+ return 0;
+}
+
+static struct attribute *ctcu_attrs[] = {
+ ctcu_byte_cntr_irq_rw(0),
+ ctcu_byte_cntr_irq_rw(1),
+ NULL,
+};
+
+static struct attribute_group ctcu_attr_grp = {
+ .attrs = ctcu_attrs,
+ .is_visible = irq_enabled_is_visible,
+};
+
+static const struct attribute_group *ctcu_attr_grps[] = {
+ &ctcu_attr_grp,
+ NULL,
+};
+
static void ctcu_program_atid_register(struct ctcu_drvdata *drvdata, u32 reg_offset,
u8 bit, bool enable)
{
@@ -140,11 +225,15 @@ static int ctcu_set_etr_traceid(struct coresight_device *csdev, struct coresight
static int ctcu_enable(struct coresight_device *csdev, enum cs_mode mode,
struct coresight_path *path)
{
+ ctcu_byte_cntr_start(csdev, path);
+
return ctcu_set_etr_traceid(csdev, path, true);
}
static int ctcu_disable(struct coresight_device *csdev, struct coresight_path *path)
{
+ ctcu_byte_cntr_stop(csdev, path);
+
return ctcu_set_etr_traceid(csdev, path, false);
}
@@ -195,7 +284,10 @@ static int ctcu_probe(struct platform_device *pdev)
for (i = 0; i < cfgs->num_etr_config; i++) {
etr_cfg = &cfgs->etr_cfgs[i];
drvdata->atid_offset[i] = etr_cfg->atid_offset;
+ drvdata->byte_cntr_data[i].irq_ctrl_offset =
+ etr_cfg->irq_ctrl_offset;
}
+ ctcu_byte_cntr_init(dev, drvdata, cfgs->num_etr_config);
}
}
@@ -209,6 +301,7 @@ static int ctcu_probe(struct platform_device *pdev)
desc.dev = dev;
desc.ops = &ctcu_ops;
desc.access = CSDEV_ACCESS_IOMEM(base);
+ desc.groups = ctcu_attr_grps;
raw_spin_lock_init(&drvdata->spin_lock);
drvdata->csdev = coresight_register(&desc);
@@ -244,10 +337,31 @@ static int ctcu_platform_probe(struct platform_device *pdev)
static void ctcu_platform_remove(struct platform_device *pdev)
{
struct ctcu_drvdata *drvdata = platform_get_drvdata(pdev);
+ struct ctcu_byte_cntr *byte_cntr_data;
+ unsigned long flags;
+ int i;
if (WARN_ON(!drvdata))
return;
+ /*
+ * Signal all active byte-cntr readers to exit, then wait for them to
+ * finish before resetting the ops pointer and freeing driver data.
+ * Without this, a reader blocked in wait_event_interruptible_timeout()
+ * would access the freed ctcu_drvdata wait-queue head (use-after-free).
+ */
+ for (i = 0; i < ETR_MAX_NUM; i++) {
+ byte_cntr_data = &drvdata->byte_cntr_data[i];
+ if (!byte_cntr_data->reading)
+ continue;
+ raw_spin_lock_irqsave(&byte_cntr_data->spin_lock, flags);
+ byte_cntr_data->enable = false;
+ raw_spin_unlock_irqrestore(&byte_cntr_data->spin_lock, flags);
+ wake_up_all(&byte_cntr_data->wq);
+ wait_event(byte_cntr_data->wq, !byte_cntr_data->reading);
+ }
+
+ tmc_etr_reset_byte_cntr_sysfs_ops();
ctcu_remove(pdev);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/hwtracing/coresight/coresight-ctcu.h b/drivers/hwtracing/coresight/coresight-ctcu.h
index e9594c38dd91..a2ae0a0d91d0 100644
--- a/drivers/hwtracing/coresight/coresight-ctcu.h
+++ b/drivers/hwtracing/coresight/coresight-ctcu.h
@@ -1,23 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2024-2026 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _CORESIGHT_CTCU_H
#define _CORESIGHT_CTCU_H
+
+#include <linux/time.h>
#include "coresight-trace-id.h"
/* Maximum number of supported ETR devices for a single CTCU. */
#define ETR_MAX_NUM 2
+#define BYTE_CNTR_TIMEOUT (3 * HZ)
+#define MAX_IRQ_CNT 20
+
/**
* struct ctcu_etr_config
* @atid_offset: offset to the ATID0 Register.
- * @port_num: in-port number of CTCU device that connected to ETR.
+ * @port_num: in-port number of the CTCU device that connected to ETR.
+ * @irq_ctrl_offset: offset to the BYTECNTRVAL register.
*/
struct ctcu_etr_config {
const u32 atid_offset;
const u32 port_num;
+ const u32 irq_ctrl_offset;
};
struct ctcu_config {
@@ -25,15 +33,68 @@ struct ctcu_config {
int num_etr_config;
};
-struct ctcu_drvdata {
- void __iomem *base;
- struct clk *apb_clk;
- struct device *dev;
- struct coresight_device *csdev;
+/**
+ * struct ctcu_byte_cntr
+ * @enable: indicates that byte_cntr function is enabled or not.
+ * @irq_enabled: indicates that the interruption is enabled.
+ * @reading: indicates that byte_cntr is reading.
+ * @irq: allocated number of the IRQ.
+ * @irq_cnt: IRQ count number of the triggered interruptions.
+ * @wq: waitqueue for reading data from ETR buffer.
+ * @spin_lock: spinlock of the byte_cntr_data.
+ * @irq_ctrl_offset: offset to the BYTECNTVAL Register.
+ * @ctcu_drvdata: drvdata of the CTCU device.
+ * @buf_node: etr_buf_node for reading.
+ */
+struct ctcu_byte_cntr {
+ bool enable;
+ bool irq_enabled;
+ bool reading;
+ int irq;
+ atomic_t irq_cnt;
+ wait_queue_head_t wq;
raw_spinlock_t spin_lock;
- u32 atid_offset[ETR_MAX_NUM];
+ u32 irq_ctrl_offset;
+ struct ctcu_drvdata *ctcu_drvdata;
+ struct etr_buf_node *buf_node;
+};
+
+struct ctcu_drvdata {
+ void __iomem *base;
+ struct clk *apb_clk;
+ struct device *dev;
+ struct coresight_device *csdev;
+ struct ctcu_byte_cntr byte_cntr_data[ETR_MAX_NUM];
+ raw_spinlock_t spin_lock;
+ u32 atid_offset[ETR_MAX_NUM];
/* refcnt for each traceid of each sink */
- u8 traceid_refcnt[ETR_MAX_NUM][CORESIGHT_TRACE_ID_RES_TOP];
+ u8 traceid_refcnt[ETR_MAX_NUM][CORESIGHT_TRACE_ID_RES_TOP];
};
+/**
+ * struct ctcu_byte_cntr_irq_attribute
+ * @attr: The device attribute.
+ * @port: port number.
+ */
+struct ctcu_byte_cntr_irq_attribute {
+ struct device_attribute attr;
+ u8 port;
+};
+
+#define ctcu_byte_cntr_irq_rw(port) \
+ (&((struct ctcu_byte_cntr_irq_attribute[]) { \
+ { \
+ __ATTR(irq_enabled##port, 0644, irq_enabled_show, \
+ irq_enabled_store), \
+ port, \
+ } \
+ })[0].attr.attr)
+
+void ctcu_program_register(struct ctcu_drvdata *drvdata, u32 val, u32 offset);
+
+/* Byte-cntr functions */
+void ctcu_byte_cntr_start(struct coresight_device *csdev, struct coresight_path *path);
+void ctcu_byte_cntr_stop(struct coresight_device *csdev, struct coresight_path *path);
+void ctcu_byte_cntr_init(struct device *dev, struct ctcu_drvdata *drvdata, int port_num);
+
#endif
diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
index 110eedde077f..9f4fd86e8c32 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-core.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
@@ -293,7 +293,8 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
return -EFAULT;
}
- *ppos += actual;
+ if (!tmc_etr_update_buf_node_pos(drvdata, actual))
+ *ppos += actual;
dev_dbg(&drvdata->csdev->dev, "%zu bytes copied\n", actual);
return actual;
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index bb76e7e37874..14e3a89432ec 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1168,6 +1168,8 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
return rc;
}
+static const struct tmc_sysfs_ops *byte_cntr_sysfs_ops;
+
/*
* Return the available trace data in the buffer (starts at etr_buf->offset,
* limited by etr_buf->len) from @pos, with a maximum limit of @len,
@@ -1178,23 +1180,39 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
* We are protected here by drvdata->reading != 0, which ensures the
* sysfs_buf stays alive.
*/
-ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
- loff_t pos, size_t len, char **bufpp)
+ssize_t tmc_etr_read_sysfs_buf(struct etr_buf *sysfs_buf, loff_t pos,
+ size_t len, char **bufpp)
{
s64 offset;
ssize_t actual = len;
- struct etr_buf *etr_buf = drvdata->sysfs_buf;
- if (pos + actual > etr_buf->len)
- actual = etr_buf->len - pos;
+ if (pos + actual > sysfs_buf->len)
+ actual = sysfs_buf->len - pos;
if (actual <= 0)
return actual;
/* Compute the offset from which we read the data */
- offset = etr_buf->offset + pos;
- if (offset >= etr_buf->size)
- offset -= etr_buf->size;
- return tmc_etr_buf_get_data(etr_buf, offset, actual, bufpp);
+ offset = sysfs_buf->offset + pos;
+ if (offset >= sysfs_buf->size)
+ offset -= sysfs_buf->size;
+ return tmc_etr_buf_get_data(sysfs_buf, offset, actual, bufpp);
+}
+EXPORT_SYMBOL_GPL(tmc_etr_read_sysfs_buf);
+
+ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
+ loff_t pos, size_t len, char **bufpp)
+{
+ ssize_t ret;
+ const struct tmc_sysfs_ops *byte_cntr_ops = READ_ONCE(byte_cntr_sysfs_ops);
+
+ if (byte_cntr_ops) {
+ ret = byte_cntr_ops->get_trace_data(drvdata, pos, len, bufpp);
+ /* Return the filled buffer */
+ if (ret > 0 || ret == -ENOMEM)
+ return ret;
+ }
+
+ return tmc_etr_read_sysfs_buf(drvdata->sysfs_buf, pos, len, bufpp);
}
static struct etr_buf *
@@ -1248,6 +1266,39 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
}
+static void tmc_etr_reset_sysfs_buf(struct tmc_drvdata *drvdata)
+{
+ u32 sts;
+
+ CS_UNLOCK(drvdata->base);
+ tmc_write_rrp(drvdata, drvdata->sysfs_buf->hwaddr);
+ tmc_write_rwp(drvdata, drvdata->sysfs_buf->hwaddr);
+ sts = readl_relaxed(drvdata->base + TMC_STS) & ~TMC_STS_FULL;
+ writel_relaxed(sts, drvdata->base + TMC_STS);
+ CS_LOCK(drvdata->base);
+}
+
+/**
+ * tmc_etr_enable_disable_hw - enable/disable the ETR hw.
+ * @drvdata: drvdata of the TMC device.
+ * @enable: indicates enable/disable.
+ */
+void tmc_etr_enable_disable_hw(struct tmc_drvdata *drvdata, bool enable)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&drvdata->spinlock, flags);
+ if (enable) {
+ tmc_etr_reset_sysfs_buf(drvdata);
+ __tmc_etr_enable_hw(drvdata);
+ } else {
+ __tmc_etr_disable_hw(drvdata);
+ }
+
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+}
+EXPORT_SYMBOL_GPL(tmc_etr_enable_disable_hw);
+
void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
{
__tmc_etr_disable_hw(drvdata);
@@ -2041,15 +2092,54 @@ int tmc_create_etr_buf_list(struct tmc_drvdata *drvdata, int num_nodes)
}
EXPORT_SYMBOL_GPL(tmc_create_etr_buf_list);
+void tmc_etr_set_byte_cntr_sysfs_ops(const struct tmc_sysfs_ops *sysfs_ops)
+{
+ WRITE_ONCE(byte_cntr_sysfs_ops, sysfs_ops);
+}
+EXPORT_SYMBOL_GPL(tmc_etr_set_byte_cntr_sysfs_ops);
+
+void tmc_etr_reset_byte_cntr_sysfs_ops(void)
+{
+ WRITE_ONCE(byte_cntr_sysfs_ops, NULL);
+}
+EXPORT_SYMBOL_GPL(tmc_etr_reset_byte_cntr_sysfs_ops);
+
+bool tmc_etr_update_buf_node_pos(struct tmc_drvdata *drvdata, ssize_t size)
+{
+ struct etr_buf_node *nd, *next;
+
+ if (drvdata->config_type != TMC_CONFIG_TYPE_ETR)
+ return false;
+
+ list_for_each_entry_safe(nd, next, &drvdata->etr_buf_list, link) {
+ if (nd && nd->reading) {
+ nd->pos += size;
+ return true;
+ }
+ }
+
+ return false;
+}
+
int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
{
int ret = 0;
unsigned long flags;
+ const struct tmc_sysfs_ops *byte_cntr_ops;
/* config types are set a boot time and never change */
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
return -EINVAL;
+ byte_cntr_ops = READ_ONCE(byte_cntr_sysfs_ops);
+ if (byte_cntr_ops) {
+ ret = byte_cntr_ops->read_prepare(drvdata);
+ if (!ret || ret == -EBUSY)
+ return ret;
+
+ ret = 0;
+ }
+
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
ret = -EBUSY;
@@ -2081,11 +2171,17 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
{
unsigned long flags;
struct etr_buf *sysfs_buf = NULL;
+ const struct tmc_sysfs_ops *byte_cntr_ops;
/* config types are set a boot time and never change */
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
return -EINVAL;
+ byte_cntr_ops = READ_ONCE(byte_cntr_sysfs_ops);
+ if (byte_cntr_ops)
+ if (!byte_cntr_ops->read_unprepare(drvdata))
+ return 0;
+
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
/* RE-enable the TMC if need be */
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index fbb015079872..a15e2f93f16a 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -211,12 +211,15 @@ struct tmc_resrv_buf {
/**
* @sysfs_buf: Allocated sysfs_buf.
* @is_free: Indicates whether the buffer is free to choose.
+ * @reading: Indicates byte_cntr is reading the buffer attached to
+ * the node.
* @pos: Offset to the start of the buffer.
* @link: list_head of the node.
*/
struct etr_buf_node {
struct etr_buf *sysfs_buf;
bool is_free;
+ bool reading;
loff_t pos;
struct list_head link;
};
@@ -480,5 +483,11 @@ struct etr_buf *tmc_etr_get_buffer(struct coresight_device *csdev,
extern const struct attribute_group coresight_etr_group;
void tmc_clean_etr_buf_list(struct tmc_drvdata *drvdata);
int tmc_create_etr_buf_list(struct tmc_drvdata *drvdata, int num_nodes);
+void tmc_etr_set_byte_cntr_sysfs_ops(const struct tmc_sysfs_ops *sysfs_ops);
+void tmc_etr_reset_byte_cntr_sysfs_ops(void);
+void tmc_etr_enable_disable_hw(struct tmc_drvdata *drvdata, bool enable);
+bool tmc_etr_update_buf_node_pos(struct tmc_drvdata *drvdata, ssize_t size);
+ssize_t tmc_etr_read_sysfs_buf(struct etr_buf *sysfs_buf, loff_t pos,
+ size_t len, char **bufpp);
#endif
--
2.34.1
^ permalink raw reply related
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