* Re: [PATCH net-next v6 2/2] net: ti: icssg-prueth: Add ethtool ops for Frame Preemption MAC Merge
From: Meghana Malladi @ 2026-06-04 13:45 UTC (permalink / raw)
To: Maxime Chevallier, liuhangbin, h-mittal1, haokexin,
vadim.fedorenko, devnexen, horms, jacob.e.keller, arnd, afd,
basharath, parvathi, vladimir.oltean, rogerq, danishanwar, pabeni,
kuba, edumazet, davem, andrew+netdev
Cc: linux-arm-kernel, netdev, linux-kernel, srk, Vignesh Raghavendra
In-Reply-To: <7f194a90-8bf6-4217-b5fa-c003b3dc85b3@bootlin.com>
On 5/26/26 01:05, Maxime Chevallier wrote:
> Hi,
>
> On 5/25/26 20:27, Meghana Malladi wrote:
>> From: MD Danish Anwar <danishanwar@ti.com>
>>
>> Add driver support for viewing and changing the MAC Merge sublayer
>> parameters via ethtool ops: .set_mm(), .get_mm() and .get_mm_stats().
>>
>> The minimum size of non-final mPacket fragments supported by the firmware
>> without leading errors is 64 Bytes (including FCS). Verify time
>> bounded to
>> 1-128 ms per 802.3-2018 clause 30.14.1.6. Add a check to ensure
>> user passed tx_min_frag_size argument via ethtool, honors this.
>> Add pa stats registers to check statistics for preemption, which can be
>> dumped using ethtool ops.
>>
>> Fix emac_get_stat_by_name() to return u64 instead of int and return 0 on
>> error instead of -EINVAL. This prevents invalid stat lookups from
>> corrupting
>> output stats with signed error codes cast to u64. Error conditions are
>> still
>> logged via netdev_err().
>>
>> Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
>> Signed-off-by: Meghana Malladi <m-malladi@ti.com>
>> ---
>>
>> v6-v5:
>> - Initialize tx_min_frag_size and verify_time with valid values
>> to avoid returning corrupted values during get_mm()
>> - Fix rx_min_frag_size to ETH_ZLEN excluding ETH_FCS_LEN
>> - Fix return codes for emac_set_mm()
>> All the above changes address the comments raised by sashiko
>>
>> drivers/net/ethernet/ti/icssg/icssg_ethtool.c | 106 ++++++++++++++++++
>> drivers/net/ethernet/ti/icssg/icssg_prueth.h | 7 +-
>> drivers/net/ethernet/ti/icssg/icssg_qos.h | 46 ++++++++
>> drivers/net/ethernet/ti/icssg/icssg_stats.c | 4 +-
>> drivers/net/ethernet/ti/icssg/icssg_stats.h | 7 +-
>> .../net/ethernet/ti/icssg/icssg_switch_map.h | 5 +
>> 6 files changed, 168 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/ti/icssg/icssg_ethtool.c b/drivers/
>> net/ethernet/ti/icssg/icssg_ethtool.c
>> index b715af21d23ac..ee940051644d6 100644
>> --- a/drivers/net/ethernet/ti/icssg/icssg_ethtool.c
>> +++ b/drivers/net/ethernet/ti/icssg/icssg_ethtool.c
>> @@ -294,6 +294,109 @@ static int emac_set_per_queue_coalesce(struct
>> net_device *ndev, u32 queue,
>> return 0;
>> }
>> +static int emac_get_mm(struct net_device *ndev, struct
>> ethtool_mm_state *state)
>> +{
>> + struct prueth_emac *emac = netdev_priv(ndev);
>> + struct prueth_qos_iet *iet = &emac->qos.iet;
>> + enum icssg_ietfpe_verify_states verify_status;
>> +
>> + if (emac->is_sr1)
>> + return -EOPNOTSUPP;
>> +
>> + mutex_lock(&iet->fpe_lock);
>> + state->tx_enabled = iet->fpe_enabled;
>> + state->tx_min_frag_size = iet->tx_min_frag_size;
>> + state->tx_active = iet->fpe_active;
>> + state->verify_enabled = iet->mac_verify_configure;
>> + state->verify_time = iet->verify_time_ms;
>> + verify_status = iet->verify_status;
>> + mutex_unlock(&iet->fpe_lock);
>> +
>> + state->rx_min_frag_size = ETH_ZLEN;
>> + state->pmac_enabled = true;
>> +
>> + switch (verify_status) {
>> + case ICSSG_IETFPE_STATE_DISABLED:
>> + state->verify_status = ETHTOOL_MM_VERIFY_STATUS_DISABLED;
>> + break;
>> + case ICSSG_IETFPE_STATE_INITIAL:
>> + state->verify_status = ETHTOOL_MM_VERIFY_STATUS_INITIAL;
>> + break;
>> + case ICSSG_IETFPE_STATE_VERIFYING:
>> + state->verify_status = ETHTOOL_MM_VERIFY_STATUS_VERIFYING;
>> + break;
>> + case ICSSG_IETFPE_STATE_SUCCEEDED:
>> + state->verify_status = ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;
>> + break;
>> + case ICSSG_IETFPE_STATE_FAILED:
>> + state->verify_status = ETHTOOL_MM_VERIFY_STATUS_FAILED;
>> + break;
>> + default:
>> + state->verify_status = ETHTOOL_MM_VERIFY_STATUS_UNKNOWN;
>> + break;
>> + }
>> +
>> + /* 802.3-2018 clause 30.14.1.6, says that the aMACMergeVerifyTime
>> + * variable has a range between 1 and 128 ms inclusive. Limit to
>> that.
>> + */
>> + state->max_verify_time = ETHTOOL_MM_MAX_VERIFY_TIME_MS;
>> +
>> + return 0;
>> +}
>> +
>> +static int emac_set_mm(struct net_device *ndev, struct ethtool_mm_cfg
>> *cfg,
>> + struct netlink_ext_ack *extack)
>> +{
>> + struct prueth_emac *emac = netdev_priv(ndev);
>> + struct prueth_qos_iet *iet = &emac->qos.iet;
>> + int err;
>> +
>> + if (emac->is_sr1)
>> + return -EOPNOTSUPP;
>> +
>> + if (!cfg->pmac_enabled) {
>> + NL_SET_ERR_MSG_MOD(extack, "preemptible MAC is always enabled");
>> + return -EOPNOTSUPP;
>> + }
>> +
>> + err = icssg_qos_validate_tx_min_frag_size(cfg->tx_min_frag_size,
>> extack);
>> + if (err)
>> + return err;
>> +
>> + err = icssg_qos_validate_verify_time(cfg->verify_time, extack);
>> + if (err)
>> + return err;
>
> The ethnl code already validates the verify_time :
>
> https://elixir.bootlin.com/linux/v7.0.9/source/net/ethtool/mm.c#L153
>
I remember getting a comment asking me to add a comment or check to
validate tx_min_frag_size, I don't remember at this point why I added
for verify_time as well, lost track in the comments lot. I will remove
it. Just to confirm, do I need icssg_qos_validate_tx_min_frag_size() or
can I remove it as well ?
>> +
>> + mutex_lock(&iet->fpe_lock);
>> + iet->verify_time_ms = cfg->verify_time;
>> + iet->tx_min_frag_size = cfg->tx_min_frag_size;
>> + iet->fpe_enabled = cfg->tx_enabled;
>> + iet->mac_verify_configure = cfg->verify_enabled;
>> + err = icssg_config_ietfpe(ndev, cfg->tx_enabled);
>> + mutex_unlock(&iet->fpe_lock);
>> +
>> + return err;
>> +}
>> +
>> +static void emac_get_mm_stats(struct net_device *ndev,
>> + struct ethtool_mm_stats *s)
>> +{
>> + struct prueth_emac *emac = netdev_priv(ndev);
>> +
>> + if (emac->is_sr1)
>> + return;
>> +
>> + if (!emac->prueth->pa_stats)
>> + return;
>> +
>> + /* MACMergeHoldCount stats is not tracked by the firmware */
>> + s->MACMergeFrameAssOkCount = emac_get_stat_by_name(emac,
>> "FW_PREEMPT_ASSEMBLY_OK");
>> + s->MACMergeFrameAssErrorCount = emac_get_stat_by_name(emac,
>> "FW_PREEMPT_ASSEMBLY_ERR");
>> + s->MACMergeFragCountRx = emac_get_stat_by_name(emac,
>> "FW_PREEMPT_FRAG_CNT_RX");
>> + s->MACMergeFragCountTx = emac_get_stat_by_name(emac,
>> "FW_PREEMPT_FRAG_CNT_TX");
>> + s->MACMergeFrameSmdErrorCount = emac_get_stat_by_name(emac,
>> "FW_PREEMPT_BAD_FRAG");
>> +}
>> +
>> const struct ethtool_ops icssg_ethtool_ops = {
>> .get_drvinfo = emac_get_drvinfo,
>> .get_msglevel = emac_get_msglevel,
>> @@ -317,5 +420,8 @@ const struct ethtool_ops icssg_ethtool_ops = {
>> .set_eee = emac_set_eee,
>> .nway_reset = emac_nway_reset,
>> .get_rmon_stats = emac_get_rmon_stats,
>> + .get_mm = emac_get_mm,
>> + .set_mm = emac_set_mm,
>> + .get_mm_stats = emac_get_mm_stats,
>> };
>> EXPORT_SYMBOL_GPL(icssg_ethtool_ops);
>> diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/
>> net/ethernet/ti/icssg/icssg_prueth.h
>> index 85f7017d2c8e7..61320c252bec2 100644
>> --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
>> +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
>> @@ -45,6 +45,7 @@
>> #include "icss_iep.h"
>> #include "icssg_switch_map.h"
>> #include "icssg_qos.h"
>> +#include "icssg_stats.h"
>> #define PRUETH_MAX_MTU (2000 - ETH_HLEN - ETH_FCS_LEN)
>> #define PRUETH_MIN_PKT_SIZE (VLAN_ETH_ZLEN)
>> @@ -58,8 +59,8 @@
>> #define ICSSG_MAX_RFLOWS 8 /* per slice */
>> -#define ICSSG_NUM_PA_STATS 32
>> -#define ICSSG_NUM_MIIG_STATS 60
>> +#define ICSSG_NUM_PA_STATS ARRAY_SIZE(icssg_all_pa_stats)
>> +#define ICSSG_NUM_MIIG_STATS ARRAY_SIZE(icssg_all_miig_stats)
>> /* Number of ICSSG related stats */
>> #define ICSSG_NUM_STATS (ICSSG_NUM_MIIG_STATS + ICSSG_NUM_PA_STATS)
>> #define ICSSG_NUM_STANDARD_STATS 31
>> @@ -460,7 +461,7 @@ int emac_fdb_flow_id_updated(struct prueth_emac
>> *emac);
>> void icssg_stats_work_handler(struct work_struct *work);
>> void emac_update_hardware_stats(struct prueth_emac *emac);
>> -int emac_get_stat_by_name(struct prueth_emac *emac, char *stat_name);
>> +u64 emac_get_stat_by_name(struct prueth_emac *emac, char *stat_name);
>> /* Common functions */
>> void prueth_cleanup_rx_chns(struct prueth_emac *emac,
>> diff --git a/drivers/net/ethernet/ti/icssg/icssg_qos.h b/drivers/net/
>> ethernet/ti/icssg/icssg_qos.h
>> index 9355e96bbcda8..87ca031afcaa4 100644
>> --- a/drivers/net/ethernet/ti/icssg/icssg_qos.h
>> +++ b/drivers/net/ethernet/ti/icssg/icssg_qos.h
>> @@ -13,6 +13,7 @@
>> #define ICSSG_EXPRESS_Q_MASK_ALL 0xFF
>> #define ICSSG_IET_MAX_VERIFY_TIME 128
>> #define ICSSG_IET_MIN_VERIFY_TIME 1
>> +#define ICSSG_IET_MAX_TX_MIN_FRAG_SIZE 252
>> /**
>> * enum icssg_ietfpe_verify_states - status of MAC Merge Verify
>> returned by firmware
>> @@ -63,4 +64,49 @@ void icssg_qos_link_state_update(struct net_device
>> *ndev);
>> int icssg_qos_ndo_setup_tc(struct net_device *ndev, enum
>> tc_setup_type type,
>> void *type_data);
>> int icssg_config_ietfpe(struct net_device *ndev, bool enable);
>> +static inline int icssg_qos_validate_tx_min_frag_size(u32 min_frag_size,
>> + struct netlink_ext_ack *extack)
>> +{
>> + /* Firmware takes min_frag_size including FCS length.
>> + * The firmware requires the fragment size (including FCS) to be
>> + * a multiple of 64 bytes. Since 64 bytes = ETH_ZLEN + ETH_FCS_LEN,
>> + * valid user-facing values are: 60, 124, 188, 252.
>> + */
>> +
>> + if (min_frag_size < ETH_ZLEN) {
>> + NL_SET_ERR_MSG_MOD(extack,
>> + "tx_min_frag_size must be at least 60 bytes");
>> + return -EINVAL;
>> + }
>> +
>> + if (min_frag_size > ICSSG_IET_MAX_TX_MIN_FRAG_SIZE) {
>> + NL_SET_ERR_MSG_MOD(extack,
>> + "tx_min_frag_size must not exceed 252 bytes");
>> + return -EINVAL;
>> + }
>> +
>> + if ((min_frag_size + ETH_FCS_LEN) % (ETH_ZLEN + ETH_FCS_LEN)) {
>> + NL_SET_ERR_MSG_MOD(extack,
>> + "tx_min_frag_size must be a multiple of 64 bytes
>> minus 4");
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>
> Is there any reason this helper is in a header file instead
> of being directly in icssg_ethtool.c ?
>
I don't have any strong reason as such, I didn't want to put any helper
functions in icssg_ethtool.c.
>> +
>> +static inline int icssg_qos_validate_verify_time(u32 verify_time_ms,
>> + struct netlink_ext_ack *extack)
>> +{
>> + /* 802.3-2018 clause 30.14.1.6: aMACMergeVerifyTime must be
>> + * between 1 and 128 ms inclusive
>> + */
>> + if (verify_time_ms < ICSSG_IET_MIN_VERIFY_TIME ||
>> + verify_time_ms > ICSSG_IET_MAX_VERIFY_TIME) {
>> + NL_SET_ERR_MSG_MOD(extack,
>> + "verify_time must be between 1 and 128 ms");
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>
> And this shouldn't be needed as the ethnl mm code already validates
> these boundaries, as they are straight from the standard.
>
Will remove this is my next version.
>> #endif /* __NET_TI_ICSSG_QOS_H */
>> diff --git a/drivers/net/ethernet/ti/icssg/icssg_stats.c b/drivers/
>> net/ethernet/ti/icssg/icssg_stats.c
>> index 7159baa0155cf..cfdb6f5dc5da1 100644
>> --- a/drivers/net/ethernet/ti/icssg/icssg_stats.c
>> +++ b/drivers/net/ethernet/ti/icssg/icssg_stats.c
>> @@ -74,7 +74,7 @@ void icssg_stats_work_handler(struct work_struct *work)
>> }
>> EXPORT_SYMBOL_GPL(icssg_stats_work_handler);
>> -int emac_get_stat_by_name(struct prueth_emac *emac, char *stat_name)
>> +u64 emac_get_stat_by_name(struct prueth_emac *emac, char *stat_name)
>> {
>> int i;
>> @@ -91,5 +91,5 @@ int emac_get_stat_by_name(struct prueth_emac *emac,
>> char *stat_name)
>> }
>> netdev_err(emac->ndev, "Invalid stats %s\n", stat_name);
>> - return -EINVAL;
>> + return 0;
>> }
>> diff --git a/drivers/net/ethernet/ti/icssg/icssg_stats.h b/drivers/
>> net/ethernet/ti/icssg/icssg_stats.h
>> index 5ec0b38e0c67d..8073deac35c3e 100644
>> --- a/drivers/net/ethernet/ti/icssg/icssg_stats.h
>> +++ b/drivers/net/ethernet/ti/icssg/icssg_stats.h
>> @@ -8,8 +8,6 @@
>> #ifndef __NET_TI_ICSSG_STATS_H
>> #define __NET_TI_ICSSG_STATS_H
>> -#include "icssg_prueth.h"
>> -
>> #define STATS_TIME_LIMIT_1G_MS 25000 /* 25 seconds @ 1G */
>> struct miig_stats_regs {
>> @@ -189,6 +187,11 @@ static const struct icssg_pa_stats
>> icssg_all_pa_stats[] = {
>> ICSSG_PA_STATS(FW_INF_DROP_PRIOTAGGED),
>> ICSSG_PA_STATS(FW_INF_DROP_NOTAG),
>> ICSSG_PA_STATS(FW_INF_DROP_NOTMEMBER),
>> + ICSSG_PA_STATS(FW_PREEMPT_BAD_FRAG),
>> + ICSSG_PA_STATS(FW_PREEMPT_ASSEMBLY_ERR),
>> + ICSSG_PA_STATS(FW_PREEMPT_FRAG_CNT_TX),
>> + ICSSG_PA_STATS(FW_PREEMPT_ASSEMBLY_OK),
>> + ICSSG_PA_STATS(FW_PREEMPT_FRAG_CNT_RX),
>> ICSSG_PA_STATS(FW_RX_EOF_SHORT_FRMERR),
>> ICSSG_PA_STATS(FW_RX_B0_DROP_EARLY_EOF),
>> ICSSG_PA_STATS(FW_TX_JUMBO_FRM_CUTOFF),
>> diff --git a/drivers/net/ethernet/ti/icssg/icssg_switch_map.h b/
>> drivers/net/ethernet/ti/icssg/icssg_switch_map.h
>> index 7e053b8af3ece..855fd4ed0b3f6 100644
>> --- a/drivers/net/ethernet/ti/icssg/icssg_switch_map.h
>> +++ b/drivers/net/ethernet/ti/icssg/icssg_switch_map.h
>> @@ -256,6 +256,11 @@
>> #define FW_INF_DROP_PRIOTAGGED 0x0148
>> #define FW_INF_DROP_NOTAG 0x0150
>> #define FW_INF_DROP_NOTMEMBER 0x0158
>> +#define FW_PREEMPT_BAD_FRAG 0x0160
>> +#define FW_PREEMPT_ASSEMBLY_ERR 0x0168
>> +#define FW_PREEMPT_FRAG_CNT_TX 0x0170
>> +#define FW_PREEMPT_ASSEMBLY_OK 0x0178
>> +#define FW_PREEMPT_FRAG_CNT_RX 0x0180
>> #define FW_RX_EOF_SHORT_FRMERR 0x0188
>> #define FW_RX_B0_DROP_EARLY_EOF 0x0190
>> #define FW_TX_JUMBO_FRM_CUTOFF 0x0198
>
> Maxime
Thanks,
Meghana
^ permalink raw reply
* Re: [PATCH v6 3/4] firmware: smccc: arm-cca-guest: Bind the TSM provider to an SMCCC device
From: Sudeep Holla @ 2026-06-04 13:45 UTC (permalink / raw)
To: Aneesh Kumar K.V
Cc: linux-coco, linux-arm-kernel, linux-kernel, Catalin Marinas,
Sudeep Holla, Greg KH, Jeremy Linton, Jonathan Cameron,
Lorenzo Pieralisi, Mark Rutland, Will Deacon, Steven Price,
Suzuki K Poulose
In-Reply-To: <yq5ase72qvwb.fsf@kernel.org>
On Thu, Jun 04, 2026 at 06:56:28PM +0530, Aneesh Kumar K.V wrote:
> Sudeep Holla <sudeep.holla@kernel.org> writes:
>
> ...
>
> > +static const struct smccc_device_info smccc_devices[] __initconst = {
> > + {
> > + .func_id = ARM_SMCCC_TRNG_VERSION,
> > + .requires_smc = false,
> > + .min_return = ARM_SMCCC_TRNG_MIN_VERSION,
> > + .device_name = "arm-smccc-trng",
> > + },
> > +};
> > +
> > +static bool __init
> > +smccc_probe_smccc_device(const struct smccc_device_info *smccc_dev)
> > +{
> > + struct arm_smccc_res res;
> > + unsigned long ret;
> > +
> > + if (!IS_ENABLED(CONFIG_ARM64))
> > + return false;
> > +
> > + if (smccc_conduit == SMCCC_CONDUIT_NONE)
> > + return false;
> > +
> > + if (smccc_dev->requires_smc && smccc_conduit != SMCCC_CONDUIT_SMC)
> > + return false;
> > +
> > + arm_smccc_1_1_invoke(smccc_dev->func_id, &res);
> > + ret = res.a0;
> > +
> > + if ((s32)ret < 0)
> > + return false;
> > +
> > + return ret >= smccc_dev->min_return;
> > +}
> > +
> >
>
> I am not sure we want the check to be as simple as ret < 0. Some
> function IDs may return input errors based on the supplied arguments
> (for example, RMI_ERROR_INPUT). In those cases, we would likely want
> this to be handled via a callback.
>
As I mentioned in response to Suzuki, we can defer that to probe of
that device. If *_VERSION, succeeds SMCCC core can add that device and
leave the rest to the core keeping the core and bus layer simple IMO.
> We also want to use conditional compilation for some function IDs.
> Given the callback approach and the #ifdefs, I wonder whether what we
> currently have is actually simpler and more flexible.”
>
I was trying to avoid conditional compilation altogether and hence the
reason for keeping it as simple as possible. Also IS_ENABLED(CONFIG_ARM64)
in above snippet must come as some condition to this generic probe.
Adding any more logic or callback defeats the bus idea here if we need
to rely/depend on multiple conditional compilation or callbacks IMO.
Let's find see if it can work with what we are adding now and may add in
near future and then decide.
--
Regards,
Sudeep
^ permalink raw reply
* Re: [PATCH v8 2/5] thermal: samsung: Add Exynos ACPM TMU driver GS101
From: Alexey Klimov @ 2026-06-04 13:52 UTC (permalink / raw)
To: Tudor Ambarus, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui,
Lukasz Luba, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski, Kees Cook,
Gustavo A. R. Silva, Peter Griffin, André Draszik,
Alim Akhtar
Cc: jyescas, linux-kernel, linux-samsung-soc, linux-pm, devicetree,
linux-hardening, linux-arm-kernel, Krzysztof Kozlowski
In-Reply-To: <20260603-acpm-tmu-v8-2-0f1810a356e6@linaro.org>
On Wed Jun 3, 2026 at 2:00 PM BST, Tudor Ambarus wrote:
> Add driver for the Thermal Management Unit (TMU) managed via the Alive
> Clock and Power Manager (ACPM), found on Samsung Exynos SoCs such as
> the Google GS101.
>
> The TMU on the GS101 utilizes a hybrid management model shared between
> the Application Processor (AP) and the ACPM firmware. The driver
> maintains direct memory-mapped access to the TMU interrupt pending
> registers to identify thermal events, while delegating functional
> tasks - such as sensor initialization, threshold configuration, and
> temperature acquisition, to the ACPM firmware via the ACPM IPC
> protocol.
>
> Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org>
> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Reviewed-by: Alexey Klimov <alexey.klimov@linaro.org>
> ---
> drivers/thermal/samsung/Kconfig | 19 ++
> drivers/thermal/samsung/Makefile | 2 +
> drivers/thermal/samsung/acpm-tmu.c | 651 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 672 insertions(+)
[...]
Best regards,
Alexey
^ permalink raw reply
* [RFC PATCH v3 0/9] accel: rocket: Add RK3568 NPU support
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
RFC, not for merge. End-to-end inference does not produce correct output
yet (see Status), so per the v2 discussion this is a request for design
feedback. It now probes, attaches, and submits cleanly on a stock
v7.1-rc6 tree; what remains is one hardware-internal issue.
The RK3568 has a single NVDLA-derived NPU core, the same IP family as the
RK3588 NPU the driver already supports; the register layout matches. The
RK3568 differences are a 32-bit NPU AXI/IOMMU (vs 40-bit) and explicit
PVTPLL/PMU bring-up to power and de-idle the NPU before it is reachable.
Patches:
1-2 rocket: per-SoC data struct, then derive DMA width and core count
from match data (refactors, no functional change).
3 rocket: RK3568 SoC data + PVTPLL/PMU/NOC bring-up.
4 rocket: reset the NPU before detaching the IOMMU on a job timeout
(the detach otherwise stalls a wedged AXI master and WARNs).
5 rocket: keep the IOMMU domain attached across jobs instead of
re-attaching per job (the per-job rk_iommu handshake on the idle
NPU MMU is slow and noisy).
6 iommu/rockchip: clear AUTO_GATING bit 1 on the RK356x v1 IOMMU so
the page-walker keeps its clock (else a TLB-miss walk never
completes).
7 dt-bindings: add the RK3568 NPU compatible.
8-9 arm64 dts: add the NPU and its IOMMU, and enable them on ROCK 3B.
Dependency. The NPU MMU is rockchip-iommu v1 (32-bit) while the rest of
the RK3568 uses v2 (40-bit). They cannot coexist until the driver carries
per-device ops; this series is developed on top of Simon Xue's
"iommu/rockchip: Drop global rk_ops in favor of per-device ops" [1].
Without it the NPU IOMMU fails to probe on a full RK3568 boot.
Power bring-up. The NPU is brought up through the power-domain layer (no
driver hack): the NPU power-domain keeps its clocks but drops the pm_qos
phandle (qos_npu sits behind the gated NPU NoC, so genpd's power-off QoS
save faults reading it), and vdd_npu is marked always-on so the rail is
up before genpd de-idles the NoC at power-on. The PMU de-idle then ACKs
without PVTPLL running; PVTPLL is only needed for compute.
Status. On v7.1-rc6 the driver probes, creates /dev/accel/accel0,
attaches an IOMMU domain, and submits jobs; the program controller
fetches and broadcasts the command list. Inference output is still wrong,
and the cause is split across three layers:
- kernel (this series): the RK3568 differences appear handled;
- mesa/Teflon userspace: still emits RK3588-tuned config, wrong for
RK3568 (to be filed separately on mesa-dev);
- hardware: with corrected config the NPU's DMA reads the full input
and weight tensors (confirmed via its DMA bandwidth counters), but
the MAC/output stage never completes, the job times out, and the
output stays at the buffer's zero-point. I have not found the missing
step; it is not in the command list (replaying the vendor's
byte-exact command list behaves the same). Pointers welcome,
especially from anyone with RK3568 NPU experience.
Known residual. On the first IOMMU attach the NPU MMU is idle with paging
already enabled; the rk_iommu stall/reset handshake does not complete in
that state and logs one burst of timeouts before the (kept) domain
settles. It is harmless here because the job times out regardless, but it
points at an idle-MMU reconfiguration corner the rk_iommu code does not
handle on this block.
[1] https://lore.kernel.org/linux-rockchip/20260310105303.128859-1-xxm@rock-chips.com/
Changes since v2:
- Tagged RFC; now tested on a stock v7.1-rc6 tree.
- Bring-up moved into the power-domain/DT layer (no initcall hack).
- Added the IOMMU detach-on-timeout and attach-once driver fixes.
- Split the driver patch (Heiko): soc_data / match-data / RK3568.
- Derive DMA width and core count from match data; drop the DT rescans.
- Binding describes the hardware; added the missing $ref on rockchip,pmu.
- Disclosed the per-device-ops IOMMU dependency.
Midgy BALON (9):
accel: rocket: Introduce per-SoC rocket_soc_data
accel: rocket: Derive DMA width and core count from match data
accel: rocket: Add RK3568 SoC support
accel: rocket: Reset the NPU before detaching the IOMMU on timeout
accel: rocket: Keep the IOMMU domain attached across jobs
iommu/rockchip: Clear AUTO_GATING bit 1 on the RK356x v1 IOMMU
dt-bindings: npu: rockchip,rk3588-rknn-core: Add RK3568
arm64: dts: rockchip: rk356x: Add the NPU and its IOMMU
arm64: dts: rockchip: rk3568-rock-3b: Enable the NPU
.../npu/rockchip,rk3588-rknn-core.yaml | 18 ++++-
.../boot/dts/rockchip/rk3568-rock-3b.dts | 14 +++-
arch/arm64/boot/dts/rockchip/rk356x-base.dtsi | 38 +++++++++++
drivers/accel/rocket/rocket_core.c | 22 ++++++-
drivers/accel/rocket/rocket_core.h | 19 ++++++
drivers/accel/rocket/rocket_device.c | 15 ++---
drivers/accel/rocket/rocket_device.h | 3 +-
drivers/accel/rocket/rocket_drv.c | 66 ++++++++++++++++++-
drivers/accel/rocket/rocket_job.c | 35 ++++++++--
drivers/iommu/rockchip-iommu.c | 12 ++++
10 files changed, 219 insertions(+), 23 deletions(-)
base-commit: 52c800fdcf11888ebeb50c3d707f782cc15b66eb
--
2.39.5
^ permalink raw reply
* [RFC PATCH v3 1/9] accel: rocket: Introduce per-SoC rocket_soc_data
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
Add a per-SoC data structure carried in the OF match table, currently
holding only the NPU AXI address width, and use it for the per-core DMA
mask instead of a hardcoded 40-bit value. No functional change: the
RK3588 AXI master is 40-bit. This prepares for SoCs with a narrower
address width.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
drivers/accel/rocket/rocket_core.c | 7 ++++++-
drivers/accel/rocket/rocket_core.h | 11 +++++++++++
drivers/accel/rocket/rocket_drv.c | 6 +++++-
3 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/drivers/accel/rocket/rocket_core.c b/drivers/accel/rocket/rocket_core.c
index b3b2fa9ba645a..09c445af7de73 100644
--- a/drivers/accel/rocket/rocket_core.c
+++ b/drivers/accel/rocket/rocket_core.c
@@ -7,6 +7,7 @@
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/iommu.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
@@ -21,6 +22,10 @@ int rocket_core_init(struct rocket_core *core)
u32 version;
int err = 0;
+ core->soc_data = of_device_get_match_data(dev);
+ if (!core->soc_data)
+ return dev_err_probe(dev, -EINVAL, "missing SoC match data\n");
+
core->resets[0].id = "srst_a";
core->resets[1].id = "srst_h";
err = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(core->resets),
@@ -52,7 +57,7 @@ int rocket_core_init(struct rocket_core *core)
dma_set_max_seg_size(dev, UINT_MAX);
- err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
+ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(core->soc_data->dma_bits));
if (err)
return err;
diff --git a/drivers/accel/rocket/rocket_core.h b/drivers/accel/rocket/rocket_core.h
index f6d7382854ca9..8ee105a0be40e 100644
--- a/drivers/accel/rocket/rocket_core.h
+++ b/drivers/accel/rocket/rocket_core.h
@@ -12,6 +12,16 @@
#include "rocket_registers.h"
+struct rocket_core;
+
+/**
+ * struct rocket_soc_data - per-SoC configuration data
+ * @dma_bits: Physical address width reachable by the NPU's AXI master.
+ */
+struct rocket_soc_data {
+ unsigned int dma_bits;
+};
+
#define rocket_pc_readl(core, reg) \
readl((core)->pc_iomem + (REG_PC_##reg))
#define rocket_pc_writel(core, reg, value) \
@@ -31,6 +41,7 @@ struct rocket_core {
struct device *dev;
struct rocket_device *rdev;
unsigned int index;
+ const struct rocket_soc_data *soc_data;
int irq;
void __iomem *pc_iomem;
diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocket_drv.c
index 8bbbce594883e..384c38e13acce 100644
--- a/drivers/accel/rocket/rocket_drv.c
+++ b/drivers/accel/rocket/rocket_drv.c
@@ -213,8 +213,12 @@ static void rocket_remove(struct platform_device *pdev)
}
}
+static const struct rocket_soc_data rk3588_soc_data = {
+ .dma_bits = 40,
+};
+
static const struct of_device_id dt_match[] = {
- { .compatible = "rockchip,rk3588-rknn-core" },
+ { .compatible = "rockchip,rk3588-rknn-core", .data = &rk3588_soc_data },
{}
};
MODULE_DEVICE_TABLE(of, dt_match);
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 2/9] accel: rocket: Derive DMA width and core count from match data
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
The probe already has the per-SoC match data, which now records the core
count and DMA width. Use it for the cores array allocation and the
device DMA mask instead of re-scanning the device tree for available core
nodes.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
drivers/accel/rocket/rocket_core.h | 2 ++
drivers/accel/rocket/rocket_device.c | 15 +++++----------
drivers/accel/rocket/rocket_device.h | 3 ++-
drivers/accel/rocket/rocket_drv.c | 7 ++++++-
4 files changed, 15 insertions(+), 12 deletions(-)
diff --git a/drivers/accel/rocket/rocket_core.h b/drivers/accel/rocket/rocket_core.h
index 8ee105a0be40e..d6421251670dc 100644
--- a/drivers/accel/rocket/rocket_core.h
+++ b/drivers/accel/rocket/rocket_core.h
@@ -16,9 +16,11 @@ struct rocket_core;
/**
* struct rocket_soc_data - per-SoC configuration data
+ * @num_cores: Number of NPU cores in this SoC.
* @dma_bits: Physical address width reachable by the NPU's AXI master.
*/
struct rocket_soc_data {
+ unsigned int num_cores;
unsigned int dma_bits;
};
diff --git a/drivers/accel/rocket/rocket_device.c b/drivers/accel/rocket/rocket_device.c
index 46e6ee1e72c5f..6186f4faa3a2a 100644
--- a/drivers/accel/rocket/rocket_device.c
+++ b/drivers/accel/rocket/rocket_device.c
@@ -6,18 +6,16 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
-#include <linux/of.h>
#include "rocket_device.h"
struct rocket_device *rocket_device_init(struct platform_device *pdev,
- const struct drm_driver *rocket_drm_driver)
+ const struct drm_driver *rocket_drm_driver,
+ const struct rocket_soc_data *soc_data)
{
struct device *dev = &pdev->dev;
- struct device_node *core_node;
struct rocket_device *rdev;
struct drm_device *ddev;
- unsigned int num_cores = 0;
int err;
rdev = devm_drm_dev_alloc(dev, rocket_drm_driver, struct rocket_device, ddev);
@@ -27,17 +25,14 @@ struct rocket_device *rocket_device_init(struct platform_device *pdev,
ddev = &rdev->ddev;
dev_set_drvdata(dev, rdev);
- for_each_compatible_node(core_node, NULL, "rockchip,rk3588-rknn-core")
- if (of_device_is_available(core_node))
- num_cores++;
-
- rdev->cores = devm_kcalloc(dev, num_cores, sizeof(*rdev->cores), GFP_KERNEL);
+ rdev->cores = devm_kcalloc(dev, soc_data->num_cores, sizeof(*rdev->cores),
+ GFP_KERNEL);
if (!rdev->cores)
return ERR_PTR(-ENOMEM);
dma_set_max_seg_size(dev, UINT_MAX);
- err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
+ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(soc_data->dma_bits));
if (err)
return ERR_PTR(err);
diff --git a/drivers/accel/rocket/rocket_device.h b/drivers/accel/rocket/rocket_device.h
index ce662abc01d3d..2f74e078974e3 100644
--- a/drivers/accel/rocket/rocket_device.h
+++ b/drivers/accel/rocket/rocket_device.h
@@ -22,7 +22,8 @@ struct rocket_device {
};
struct rocket_device *rocket_device_init(struct platform_device *pdev,
- const struct drm_driver *rocket_drm_driver);
+ const struct drm_driver *rocket_drm_driver,
+ const struct rocket_soc_data *soc_data);
void rocket_device_fini(struct rocket_device *rdev);
#define to_rocket_device(drm_dev) \
((struct rocket_device *)(container_of((drm_dev), struct rocket_device, ddev)))
diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocket_drv.c
index 384c38e13acce..c18840e5aff76 100644
--- a/drivers/accel/rocket/rocket_drv.c
+++ b/drivers/accel/rocket/rocket_drv.c
@@ -159,11 +159,15 @@ static const struct drm_driver rocket_drm_driver = {
static int rocket_probe(struct platform_device *pdev)
{
+ const struct rocket_soc_data *soc_data = of_device_get_match_data(&pdev->dev);
int ret;
+ if (!soc_data)
+ return -EINVAL;
+
if (rdev == NULL) {
/* First core probing, initialize DRM device. */
- rdev = rocket_device_init(drm_dev, &rocket_drm_driver);
+ rdev = rocket_device_init(drm_dev, &rocket_drm_driver, soc_data);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "failed to initialize rocket device\n");
return PTR_ERR(rdev);
@@ -214,6 +218,7 @@ static void rocket_remove(struct platform_device *pdev)
}
static const struct rocket_soc_data rk3588_soc_data = {
+ .num_cores = 3,
.dma_bits = 40,
};
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 5/9] accel: rocket: Keep the IOMMU domain attached across jobs
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
rocket attached the job's IOMMU domain in rocket_job_run() and
detached it again on every completion and reset. Each attach/detach
toggles the rk_iommu stall/force-reset/paging handshake, and on
RK3568 the NPU MMU is idle between jobs, so that handshake times out
and logs a burst of "stall/paging request timed out" errors for
every job.
Attach the per-context domain once and keep it: track the attached
domain in the core, swap it only when a job from a different context
runs, and detach it at core teardown. A reference on the attached
domain is held so it outlives the job that first attached it and is
released on swap/teardown.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
drivers/accel/rocket/rocket_core.c | 6 ++++++
drivers/accel/rocket/rocket_core.h | 3 +++
drivers/accel/rocket/rocket_job.c | 27 +++++++++++++++++++++------
3 files changed, 30 insertions(+), 6 deletions(-)
diff --git a/drivers/accel/rocket/rocket_core.c b/drivers/accel/rocket/rocket_core.c
index a8de876365873..634f78dfe2887 100644
--- a/drivers/accel/rocket/rocket_core.c
+++ b/drivers/accel/rocket/rocket_core.c
@@ -13,6 +13,7 @@
#include <linux/reset.h>
#include "rocket_core.h"
+#include "rocket_drv.h"
#include "rocket_job.h"
int rocket_core_init(struct rocket_core *core)
@@ -112,6 +113,11 @@ void rocket_core_fini(struct rocket_core *core)
{
pm_runtime_dont_use_autosuspend(core->dev);
pm_runtime_disable(core->dev);
+ if (core->attached_domain) {
+ iommu_detach_group(NULL, core->iommu_group);
+ rocket_iommu_domain_put(core->attached_domain);
+ core->attached_domain = NULL;
+ }
iommu_group_put(core->iommu_group);
core->iommu_group = NULL;
rocket_job_fini(core);
diff --git a/drivers/accel/rocket/rocket_core.h b/drivers/accel/rocket/rocket_core.h
index 66d138a8ed773..05a197a9c0113 100644
--- a/drivers/accel/rocket/rocket_core.h
+++ b/drivers/accel/rocket/rocket_core.h
@@ -42,6 +42,8 @@ struct rocket_soc_data {
#define rocket_core_writel(core, reg, value) \
writel(value, (core)->core_iomem + (REG_CORE_##reg) - REG_CORE_S_STATUS)
+struct rocket_iommu_domain;
+
struct rocket_core {
struct device *dev;
struct rocket_device *rdev;
@@ -56,6 +58,7 @@ struct rocket_core {
struct reset_control_bulk_data resets[2];
struct iommu_group *iommu_group;
+ struct rocket_iommu_domain *attached_domain;
struct mutex job_lock;
struct rocket_job *in_flight_job;
diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c
index e25234261536b..b248371be8a1e 100644
--- a/drivers/accel/rocket/rocket_job.c
+++ b/drivers/accel/rocket/rocket_job.c
@@ -9,6 +9,7 @@
#include <drm/rocket_accel.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
+#include <linux/kref.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -314,9 +315,26 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
if (ret < 0)
return fence;
- ret = iommu_attach_group(job->domain->domain, core->iommu_group);
- if (ret < 0)
- return fence;
+ /*
+ * Attach the job's IOMMU domain only when it differs from the one
+ * already attached. Re-attaching per job toggles the rk_iommu
+ * stall/reset handshake on an idle NPU MMU, which is slow and
+ * noisy; keep the domain attached across jobs instead.
+ */
+ if (core->attached_domain != job->domain) {
+ if (core->attached_domain) {
+ iommu_detach_group(NULL, core->iommu_group);
+ rocket_iommu_domain_put(core->attached_domain);
+ core->attached_domain = NULL;
+ }
+
+ ret = iommu_attach_group(job->domain->domain, core->iommu_group);
+ if (ret < 0)
+ return fence;
+
+ kref_get(&job->domain->kref);
+ core->attached_domain = job->domain;
+ }
scoped_guard(mutex, &core->job_lock) {
core->in_flight_job = job;
@@ -340,7 +358,6 @@ static void rocket_job_handle_irq(struct rocket_core *core)
return;
}
- iommu_detach_group(NULL, iommu_group_get(core->dev));
dma_fence_signal(core->in_flight_job->done_fence);
pm_runtime_put_autosuspend(core->dev);
core->in_flight_job = NULL;
@@ -376,8 +393,6 @@ rocket_reset(struct rocket_core *core, struct drm_sched_job *bad)
*/
rocket_core_reset(core);
- iommu_detach_group(NULL, core->iommu_group);
-
/* NPU has been reset, we can clear the reset pending bit. */
atomic_set(&core->reset.pending, 0);
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 4/9] accel: rocket: Reset the NPU before detaching the IOMMU on timeout
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
On a job timeout the NPU AXI master can be left wedged with
outstanding transactions. rocket_reset() detached the IOMMU group
before resetting the hardware, so iommu_detach_group() ->
__iommu_group_set_core_domain() asked the rk_iommu to stall and wait
for the in-flight transactions to drain. They never did, the stall
request timed out (-ETIMEDOUT) and the IOMMU core WARNed:
WARNING: drivers/iommu/iommu.c:157 __iommu_group_set_core_domain
iommu_detach_group
rocket_reset
rocket_job_timedout
Assert the core reset first: it quiesces the AXI master so the
following IOMMU detach completes cleanly. Move the detach after
rocket_core_reset() and out of the job_lock (it does not touch
in_flight_job).
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
drivers/accel/rocket/rocket_job.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c
index ac51bff39833f..e25234261536b 100644
--- a/drivers/accel/rocket/rocket_job.c
+++ b/drivers/accel/rocket/rocket_job.c
@@ -364,14 +364,20 @@ rocket_reset(struct rocket_core *core, struct drm_sched_job *bad)
if (core->in_flight_job)
pm_runtime_put_noidle(core->dev);
- iommu_detach_group(NULL, core->iommu_group);
-
core->in_flight_job = NULL;
}
- /* Proceed with reset now. */
+ /*
+ * Reset the NPU hardware before detaching the IOMMU. A timed-out job
+ * leaves the NPU AXI master wedged; detaching the IOMMU then issues a
+ * stall request that never drains and times out (warning in the IOMMU
+ * core). Asserting the core reset first quiesces the master so the
+ * detach completes cleanly.
+ */
rocket_core_reset(core);
+ iommu_detach_group(NULL, core->iommu_group);
+
/* NPU has been reset, we can clear the reset pending bit. */
atomic_set(&core->reset.pending, 0);
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 3/9] accel: rocket: Add RK3568 SoC support
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
The RK3568 has a single core of the same NVDLA-derived NPU IP as the
RK3588, with a 32-bit AXI master. Unlike the RK3588 it must be powered
on and de-idled through the PMU, and its PVTPLL clock started via SCMI,
before the NPU is reachable. Add rk3568_soc_data with an noc_init
callback performing this bring-up.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
drivers/accel/rocket/rocket_core.c | 9 +++++
drivers/accel/rocket/rocket_core.h | 3 ++
drivers/accel/rocket/rocket_drv.c | 53 ++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+)
diff --git a/drivers/accel/rocket/rocket_core.c b/drivers/accel/rocket/rocket_core.c
index 09c445af7de73..a8de876365873 100644
--- a/drivers/accel/rocket/rocket_core.c
+++ b/drivers/accel/rocket/rocket_core.c
@@ -88,6 +88,15 @@ int rocket_core_init(struct rocket_core *core)
return err;
}
+ if (core->soc_data->noc_init) {
+ err = core->soc_data->noc_init(core);
+ if (err) {
+ pm_runtime_put_sync(dev);
+ rocket_job_fini(core);
+ return err;
+ }
+ }
+
version = rocket_pc_readl(core, VERSION);
version += rocket_pc_readl(core, VERSION_NUM) & 0xffff;
diff --git a/drivers/accel/rocket/rocket_core.h b/drivers/accel/rocket/rocket_core.h
index d6421251670dc..66d138a8ed773 100644
--- a/drivers/accel/rocket/rocket_core.h
+++ b/drivers/accel/rocket/rocket_core.h
@@ -18,10 +18,13 @@ struct rocket_core;
* struct rocket_soc_data - per-SoC configuration data
* @num_cores: Number of NPU cores in this SoC.
* @dma_bits: Physical address width reachable by the NPU's AXI master.
+ * @noc_init: Optional callback to power on and de-idle the NPU NOC bus.
+ * Required on RK3568, where this is done through the PMU.
*/
struct rocket_soc_data {
unsigned int num_cores;
unsigned int dma_bits;
+ int (*noc_init)(struct rocket_core *core);
};
#define rocket_pc_readl(core, reg) \
diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocket_drv.c
index c18840e5aff76..5a72d0b5f4dff 100644
--- a/drivers/accel/rocket/rocket_drv.c
+++ b/drivers/accel/rocket/rocket_drv.c
@@ -9,9 +9,11 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/iommu.h>
+#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include "rocket_device.h"
#include "rocket_drv.h"
@@ -217,12 +219,63 @@ static void rocket_remove(struct platform_device *pdev)
}
}
+/*
+ * On RK3568 the NPU NOC bus is gated and idle out of reset and must be
+ * powered on and de-idled through the PMU before the NPU is reachable. PMU
+ * registers use a write-mask protocol: the upper 16 bits enable writes to the
+ * matching lower 16 bits.
+ *
+ * The NPU's high-speed clock is a PVTPLL managed by TF-A via SCMI and must be
+ * running before the NOC acknowledges the de-idle request. Force a real SCMI
+ * rate change (an intermediate rate defeats the clock framework's
+ * unchanged-rate shortcut) now that the power domain is on and clocks enabled.
+ */
+#define ROCKET_RK3568_SCMI_CLK 2
+
+static int rk3568_noc_init(struct rocket_core *core)
+{
+ struct regmap *pmu;
+ unsigned int val;
+ int ret;
+
+ clk_set_rate(core->clks[ROCKET_RK3568_SCMI_CLK].clk, 600000000UL);
+ clk_set_rate(core->clks[ROCKET_RK3568_SCMI_CLK].clk, 1000000000UL);
+
+ pmu = syscon_regmap_lookup_by_phandle(core->dev->of_node, "rockchip,pmu");
+ if (IS_ERR(pmu))
+ return dev_err_probe(core->dev, PTR_ERR(pmu),
+ "failed to get PMU regmap\n");
+
+ /* Power on the NPU power domain (PWR_GATE_SFTCON bit 1 = 0). */
+ regmap_write(pmu, 0xa0, BIT(1 + 16));
+
+ /* Disable NPU NOC auto-idle (NOC_AUTO_CON0 bit 2). */
+ regmap_write(pmu, 0x70, BIT(2 + 16));
+
+ /* Request NPU bus de-idle (BUS_IDLE_SFTCON0 bit 2 = 0). */
+ regmap_write(pmu, 0x50, BIT(2 + 16));
+
+ /* Wait for the bus to report active (BUS_IDLE_ST bit 2 = 0). */
+ ret = regmap_read_poll_timeout(pmu, 0x68, val, !(val & BIT(2)), 10, 1000);
+ if (ret)
+ dev_err(core->dev, "timed out waiting for NPU bus de-idle\n");
+
+ return ret;
+}
+
+static const struct rocket_soc_data rk3568_soc_data = {
+ .num_cores = 1,
+ .dma_bits = 32,
+ .noc_init = rk3568_noc_init,
+};
+
static const struct rocket_soc_data rk3588_soc_data = {
.num_cores = 3,
.dma_bits = 40,
};
static const struct of_device_id dt_match[] = {
+ { .compatible = "rockchip,rk3568-rknn-core", .data = &rk3568_soc_data },
{ .compatible = "rockchip,rk3588-rknn-core", .data = &rk3588_soc_data },
{}
};
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 6/9] iommu/rockchip: Clear AUTO_GATING bit 1 on the RK356x v1 IOMMU
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
On the RK356x v1 IOMMU, RK_MMU_AUTO_GATING resets to 0x3. Bit 1 enables
auto clock-gating of the page-table walker, so the walker's AXI master
loses its clock between transactions; a TLB-miss page walk then never
completes and the IOMMU is left stuck (PAGING_ENABLED, never IDLE).
Clear bit 1 (keeping bit 0, the slave-port gate) once paging is enabled
so the walker keeps its clock. This is required for the RK3568 NPU MMU.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
drivers/iommu/rockchip-iommu.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 4da80136933c4..e3d8b6e9ca12b 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -953,6 +953,18 @@ static int rk_iommu_enable(struct rk_iommu *iommu)
ret = rk_iommu_enable_paging(iommu);
+ if (!ret) {
+ /*
+ * RK356x v1 IOMMU: RK_MMU_AUTO_GATING bit 1 enables page-walker
+ * auto clock-gating; the walker's AXI master then loses its clock
+ * between transactions and a TLB-miss page walk never completes,
+ * leaving the IOMMU stuck (PAGING_ENABLED, never IDLE). Clear
+ * bit 1 (keep bit 0, the slave-port gate) once paging is enabled.
+ */
+ for (i = 0; i < iommu->num_mmu; i++)
+ rk_iommu_write(iommu->bases[i], RK_MMU_AUTO_GATING, 0x2);
+ }
+
out_disable_stall:
rk_iommu_disable_stall(iommu);
out_disable_clocks:
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 9/9] arm64: dts: rockchip: rk3568-rock-3b: Enable the NPU
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
Enable the NPU and its IOMMU on ROCK 3B.
vdd_npu is marked always-on so the rail is up before genpd de-idles the
NPU NoC at power-on: the PMU de-idle handshake needs the rail powered.
The PVTPLL compute clock is brought up later by the driver.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
arch/arm64/boot/dts/rockchip/rk3568-rock-3b.dts | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3568-rock-3b.dts b/arch/arm64/boot/dts/rockchip/rk3568-rock-3b.dts
index 69001e453732e..7ac780ed313d5 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-rock-3b.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-rock-3b.dts
@@ -330,8 +330,10 @@ regulator-state-mem {
vdd_npu: DCDC_REG4 {
regulator-name = "vdd_npu";
+ regulator-always-on;
+ regulator-boot-on;
regulator-initial-mode = <0x2>;
- regulator-min-microvolt = <500000>;
+ regulator-min-microvolt = <825000>;
regulator-max-microvolt = <1350000>;
regulator-ramp-delay = <6001>;
@@ -787,3 +789,13 @@ vp0_out_hdmi: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
remote-endpoint = <&hdmi_in_vp0>;
};
};
+
+&rknn_core_0 {
+ npu-supply = <&vdd_npu>;
+ status = "okay";
+};
+
+&rknn_mmu_0 {
+ status = "okay";
+};
+
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 8/9] arm64: dts: rockchip: rk356x: Add the NPU and its IOMMU
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
The RK3568 has an NVDLA-derived NPU at fde40000 with its own IOMMU at
fde4b000. Add both nodes (disabled by default) and the NPU power-domain
child under the PMU power-controller, and point rockchip,pmu at the PMU
syscon that controls the NPU NoC bus-idle.
The power-domain deliberately carries no pm_qos: qos_npu sits behind the
NPU NoC, which is gated until the NPU is brought up, so a genpd power-off
QoS save would fault reading it.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
arch/arm64/boot/dts/rockchip/rk356x-base.dtsi | 38 +++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi b/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi
index 64bdd8b7754b5..50ce5a5e4fc24 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi
@@ -512,6 +512,13 @@ power-domain@RK3568_PD_GPU {
#power-domain-cells = <0>;
};
+ pd_npu: power-domain@RK3568_PD_NPU {
+ reg = <RK3568_PD_NPU>;
+ clocks = <&cru ACLK_NPU_PRE>,
+ <&cru HCLK_NPU_PRE>;
+ #power-domain-cells = <0>;
+ };
+
/* These power domains are grouped by VD_LOGIC */
power-domain@RK3568_PD_VI {
reg = <RK3568_PD_VI>;
@@ -948,6 +955,37 @@ qos_rga_wr: qos@fe158300 {
reg = <0x0 0xfe158300 0x0 0x20>;
};
+ rknn_core_0: npu@fde40000 {
+ compatible = "rockchip,rk3568-rknn-core";
+ reg = <0x0 0xfde40000 0x0 0x1000>,
+ <0x0 0xfde41000 0x0 0x1000>,
+ <0x0 0xfde43000 0x0 0x1000>;
+ reg-names = "pc", "cna", "core";
+ interrupts = <GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_NPU>, <&cru HCLK_NPU>,
+ <&scmi_clk SCMI_CLK_NPU>, <&cru PCLK_NPU_PRE>;
+ clock-names = "aclk", "hclk", "npu", "pclk";
+ assigned-clocks = <&scmi_clk SCMI_CLK_NPU>;
+ assigned-clock-rates = <200000000>;
+ resets = <&cru SRST_A_NPU>, <&cru SRST_H_NPU>;
+ reset-names = "srst_a", "srst_h";
+ power-domains = <&power RK3568_PD_NPU>;
+ rockchip,pmu = <&pmu>;
+ iommus = <&rknn_mmu_0>;
+ status = "disabled";
+ };
+
+ rknn_mmu_0: iommu@fde4b000 {
+ compatible = "rockchip,iommu";
+ reg = <0x0 0xfde4b000 0x0 0x40>;
+ interrupts = <GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>;
+ clock-names = "aclk", "iface";
+ clocks = <&cru ACLK_NPU>, <&cru HCLK_NPU>;
+ power-domains = <&power RK3568_PD_NPU>;
+ #iommu-cells = <0>;
+ status = "disabled";
+ };
+
qos_npu: qos@fe180000 {
compatible = "rockchip,rk3568-qos", "syscon";
reg = <0x0 0xfe180000 0x0 0x20>;
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 7/9] dt-bindings: npu: rockchip,rk3588-rknn-core: Add RK3568
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
The RK3568 carries a single core of the same NVDLA-derived NPU IP as the
RK3588. Add its compatible.
On RK3568 the NPU NOC bus-idle and power gating are controlled through the
system PMU rather than a dedicated register block, so add a rockchip,pmu
phandle to that syscon. The RK3568 NPU has no dedicated SRAM rail, so
sram-supply is required only on RK3588.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
.../npu/rockchip,rk3588-rknn-core.yaml | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml b/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
index caca2a4903cd1..af9936b32e9fe 100644
--- a/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
+++ b/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
@@ -21,6 +21,7 @@ properties:
compatible:
enum:
+ - rockchip,rk3568-rknn-core
- rockchip,rk3588-rknn-core
reg:
@@ -50,6 +51,13 @@ properties:
npu-supply: true
+ rockchip,pmu:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the PMU syscon. On RK3568 the NPU's NOC bus-idle and
+ power gating are controlled through the PMU; this points to that
+ syscon so those registers can be reached.
+
power-domains:
maxItems: 1
@@ -75,7 +83,15 @@ required:
- resets
- reset-names
- npu-supply
- - sram-supply
+
+if:
+ properties:
+ compatible:
+ contains:
+ const: rockchip,rk3588-rknn-core
+then:
+ required:
+ - sram-supply
additionalProperties: false
--
2.39.5
^ permalink raw reply related
* RE: [PATCH v5 05/20] dma-pool: track decrypted atomic pools and select them via attrs
From: Michael Kelley @ 2026-06-04 14:05 UTC (permalink / raw)
To: Jason Gunthorpe, Michael Kelley
Cc: Aneesh Kumar K.V, iommu@lists.linux.dev,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev,
Robin Murphy, Marek Szyprowski, Will Deacon, Marc Zyngier,
Steven Price, Suzuki K Poulose, Catalin Marinas, Jiri Pirko,
Mostafa Saleh, Petr Tesarik, Alexey Kardashevskiy, Dan Williams,
Xu Yilun, linuxppc-dev@lists.ozlabs.org,
linux-s390@vger.kernel.org, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86@kernel.org, Jiri Pirko
In-Reply-To: <20260603005454.GM2487554@ziepe.ca>
From: Jason Gunthorpe <jgg@ziepe.ca> Sent: Tuesday, June 2, 2026 5:55 PM
>
> On Tue, Jun 02, 2026 at 02:24:40PM +0000, Michael Kelley wrote:
>
> > Except that in a normal VM, the "unencrypted" pool attribute does *not*
> > describe the state of the memory itself. In a normal VM, the memory is
> > unencrypted, but the "unencrypted" pool attribute is false. That
> > contradiction is the essence of my concern.
>
> I would argue no..
>
> When CC is enabled the default state of memory in a Linux environment
> is "encrypted". You have to take a special action to "decrypt" it.
>
> Thus the default state of memory in a non-CC environment is also
> paradoxically "encrypted" too.
The need to have such an unnatural premise is usually an indication
of a conceptual problem with the overall model, or perhaps just a
terminology problem.
Here's a proposal. The new DMA attribute is DMA_ATTR_CC_SHARED.
Name the pool attribute "cc_shared" instead of "unencrypted". Having
"cc_shared" set to false in a normal VM doesn't lead to the non-sensical
situation of claiming that a normal VM is encrypted. The boolean
"unencrypted" parameter that has been added to various calls also
becomes "cc_shared". If "CC_SHARED" is a suitable name for the DMA
attribute, it ought to be suitable as the pool attribute. And everything
matches as well.
Michael
> "decryption" is impossible.
>
> Therefore the "unencrypted" state is a special state that only memory
> inside a CC VM can have. A normal VM can never have "unencrypted"
> memory at all, so having it be false in the pool is accurate as far as
> the APIs go.
>
> un-encrypted = true means "the memory in this pool was transformed with
> set_memory_decrypted()" - which is impossible on a normal VM.
>
> Jason
^ permalink raw reply
* [PATCH] docs: arm64: Document that text_offset is always 0
From: Rasmus Villemoes @ 2026-06-04 14:08 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Ard Biesheuvel, Will Deacon, Jonathan Corbet, linux-doc,
linux-kernel, Rasmus Villemoes
When trying to figure out where to place and call an arm64 Image in
memory, reading booting.rst should provide the answer. However, it
requires quite some digging to figure out that text_offset is set via
".quad 0" in head.S and is thus actually always 0 since v5.10.
Update the documentation and make that explicit. Reword the 2MB
requirement accordingly, and remove the paragraphs that only apply to
the ancient versions where text_offset could be non-zero, as they only
confuse a current reader.
Fixes: 120dc60d0bdb ("arm64: get rid of TEXT_OFFSET")
Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
---
I've included a Fixes tag since I spent way too much time tracking
down where that text_offset might be defined. The mentioned commit did
get rid of all references to TEXT_OFFSET-the-macro, but not
text_offset-the-concept.
Documentation/arch/arm64/booting.rst | 20 +++++---------------
1 file changed, 5 insertions(+), 15 deletions(-)
diff --git a/Documentation/arch/arm64/booting.rst b/Documentation/arch/arm64/booting.rst
index 13ef311dace8..f4cc25b1fd56 100644
--- a/Documentation/arch/arm64/booting.rst
+++ b/Documentation/arch/arm64/booting.rst
@@ -55,9 +55,6 @@ not exceed 2 megabytes in size. Since the dtb will be mapped cacheable
using blocks of up to 2 megabytes in size, it must not be placed within
any 2M region which must be mapped with any specific attributes.
-NOTE: versions prior to v4.2 also require that the DTB be placed within
-the 512 MB region starting at text_offset bytes below the kernel Image.
-
3. Decompress the kernel image
------------------------------
@@ -93,6 +90,8 @@ Header notes:
- As of v3.17, all fields are little endian unless stated otherwise.
+- As of v5.10, text_offset is always 0.
+
- code0/code1 are responsible for branching to stext.
- when booting through EFI, code0/code1 are initially skipped.
@@ -100,12 +99,6 @@ Header notes:
entry point (efi_stub_entry). When the stub has done its work, it
jumps to code0 to resume the normal boot process.
-- Prior to v3.17, the endianness of text_offset was not specified. In
- these cases image_size is zero and text_offset is 0x80000 in the
- endianness of the kernel. Where image_size is non-zero image_size is
- little-endian and must be respected. Where image_size is zero,
- text_offset can be assumed to be 0x80000.
-
- The flags field (introduced in v3.17) is a little-endian 64-bit field
composed as follows:
@@ -135,12 +128,9 @@ Header notes:
end of the kernel image. The amount of space required will vary
depending on selected features, and is effectively unbound.
-The Image must be placed text_offset bytes from a 2MB aligned base
-address anywhere in usable system RAM and called there. The region
-between the 2 MB aligned base address and the start of the image has no
-special significance to the kernel, and may be used for other purposes.
-At least image_size bytes from the start of the image must be free for
-use by the kernel.
+The Image must be placed at a 2MB aligned base address anywhere in
+usable system RAM and called there. At least image_size bytes from
+the start of the image must be free for use by the kernel.
NOTE: versions prior to v4.6 cannot make use of memory below the
physical offset of the Image so it is recommended that the Image be
placed as close as possible to the start of system RAM.
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v6 3/8] perf cs-etm: Use thread-stack for last branch entries
From: James Clark @ 2026-06-04 14:09 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-3-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> CS ETM maintains its own circular array for last branch entries, with
> local helpers to update, copy and reset the branch stack. This duplicates
> logic already provided by the common code.
>
> Record branch with thread_stack__event() and synthesize branch stack
> with thread_stack__br_sample(). This removes the local last_branch_rb
> buffer and position tracking. Keep the buffer number updated via
> thread_stack__set_trace_nr(), which is used when exporting samples to
> Python scripts.
>
> The output should remain same, except that be->flags.predicted is no
> longer set. Since CoreSight trace does not provide branch prediction
> information, clearing the flag avoids confusion.
>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 152 +++++++++++++----------------------------------
> 1 file changed, 41 insertions(+), 111 deletions(-)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index 5bff8811d61e423463b7bd4e20d599d5b5307a1a..398ab3b7a429d402cc8e5f6cccb35c0b7c253732 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -83,14 +83,13 @@ struct cs_etm_auxtrace {
> struct cs_etm_traceid_queue {
> u8 trace_chan_id;
> u64 period_instructions;
> - size_t last_branch_pos;
> union perf_event *event_buf;
> struct thread *thread;
> struct thread *prev_packet_thread;
> ocsd_ex_level prev_packet_el;
> ocsd_ex_level el;
> + unsigned int br_stack_sz;
> struct branch_stack *last_branch;
> - struct branch_stack *last_branch_rb;
> struct cs_etm_packet *prev_packet;
> struct cs_etm_packet *packet;
> struct cs_etm_packet_queue packet_queue;
> @@ -635,9 +634,8 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
> tidq->last_branch = zalloc(sz);
> if (!tidq->last_branch)
> goto out_free;
> - tidq->last_branch_rb = zalloc(sz);
> - if (!tidq->last_branch_rb)
> - goto out_free;
> +
> + tidq->br_stack_sz = etm->synth_opts.last_branch_sz;
> }
>
> tidq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
> @@ -647,7 +645,6 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
> return 0;
>
> out_free:
> - zfree(&tidq->last_branch_rb);
> zfree(&tidq->last_branch);
> zfree(&tidq->prev_packet);
> zfree(&tidq->packet);
> @@ -941,7 +938,6 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq)
> thread__zput(tidq->prev_packet_thread);
> zfree(&tidq->event_buf);
> zfree(&tidq->last_branch);
> - zfree(&tidq->last_branch_rb);
> zfree(&tidq->prev_packet);
> zfree(&tidq->packet);
> zfree(&tidq);
> @@ -1281,57 +1277,6 @@ static int cs_etm__queue_first_cs_timestamp(struct cs_etm_auxtrace *etm,
> return ret;
> }
>
> -static inline
> -void cs_etm__copy_last_branch_rb(struct cs_etm_queue *etmq,
> - struct cs_etm_traceid_queue *tidq)
> -{
> - struct branch_stack *bs_src = tidq->last_branch_rb;
> - struct branch_stack *bs_dst = tidq->last_branch;
> - size_t nr = 0;
> -
> - /*
> - * Set the number of records before early exit: ->nr is used to
> - * determine how many branches to copy from ->entries.
> - */
> - bs_dst->nr = bs_src->nr;
> -
> - /*
> - * Early exit when there is nothing to copy.
> - */
> - if (!bs_src->nr)
> - return;
> -
> - /*
> - * As bs_src->entries is a circular buffer, we need to copy from it in
> - * two steps. First, copy the branches from the most recently inserted
> - * branch ->last_branch_pos until the end of bs_src->entries buffer.
> - */
> - nr = etmq->etm->synth_opts.last_branch_sz - tidq->last_branch_pos;
> - memcpy(&bs_dst->entries[0],
> - &bs_src->entries[tidq->last_branch_pos],
> - sizeof(struct branch_entry) * nr);
> -
> - /*
> - * If we wrapped around at least once, the branches from the beginning
> - * of the bs_src->entries buffer and until the ->last_branch_pos element
> - * are older valid branches: copy them over. The total number of
> - * branches copied over will be equal to the number of branches asked by
> - * the user in last_branch_sz.
> - */
> - if (bs_src->nr >= etmq->etm->synth_opts.last_branch_sz) {
> - memcpy(&bs_dst->entries[nr],
> - &bs_src->entries[0],
> - sizeof(struct branch_entry) * tidq->last_branch_pos);
> - }
> -}
> -
> -static inline
> -void cs_etm__reset_last_branch_rb(struct cs_etm_traceid_queue *tidq)
> -{
> - tidq->last_branch_pos = 0;
> - tidq->last_branch_rb->nr = 0;
> -}
> -
> static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq,
> u8 trace_chan_id, u64 addr)
> {
> @@ -1400,38 +1345,6 @@ static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq,
> return addr;
> }
>
> -static void cs_etm__update_last_branch_rb(struct cs_etm_queue *etmq,
> - struct cs_etm_traceid_queue *tidq)
> -{
> - struct branch_stack *bs = tidq->last_branch_rb;
> - struct branch_entry *be;
> -
> - /*
> - * The branches are recorded in a circular buffer in reverse
> - * chronological order: we start recording from the last element of the
> - * buffer down. After writing the first element of the stack, move the
> - * insert position back to the end of the buffer.
> - */
> - if (!tidq->last_branch_pos)
> - tidq->last_branch_pos = etmq->etm->synth_opts.last_branch_sz;
> -
> - tidq->last_branch_pos -= 1;
> -
> - be = &bs->entries[tidq->last_branch_pos];
> - be->from = cs_etm__last_executed_instr(tidq->prev_packet);
> - be->to = cs_etm__first_executed_instr(tidq->packet);
> - /* No support for mispredict */
> - be->flags.mispred = 0;
> - be->flags.predicted = 1;
> -
> - /*
> - * Increment bs->nr until reaching the number of last branches asked by
> - * the user on the command line.
> - */
> - if (bs->nr < etmq->etm->synth_opts.last_branch_sz)
> - bs->nr += 1;
> -}
> -
> static int cs_etm__inject_event(struct cs_etm_auxtrace *etm, union perf_event *event,
> struct perf_sample *sample, u64 type)
> {
> @@ -1579,6 +1492,37 @@ static inline u64 cs_etm__resolve_sample_time(struct cs_etm_queue *etmq,
> return etm->latest_kernel_timestamp;
> }
>
> +static void cs_etm__add_stack_event(struct cs_etm_queue *etmq,
> + struct cs_etm_traceid_queue *tidq)
> +{
> + u64 from, to;
> + int size;
> +
> + if (!tidq->prev_packet->last_instr_taken_branch)
> + return;
> +
> + if (tidq->prev_packet->sample_type != CS_ETM_RANGE ||
> + tidq->packet->sample_type != CS_ETM_RANGE)
> + return;
> +
> + if (etmq->etm->synth_opts.last_branch) {
> + from = cs_etm__last_executed_instr(tidq->prev_packet);
> + to = cs_etm__first_executed_instr(tidq->packet);
> +
> + size = cs_etm__instr_size(etmq, tidq->trace_chan_id,
> + tidq->prev_packet->isa, from);
> +
> + /* Enable callchain so thread stack entry can be allocated */
> + thread_stack__event(tidq->thread, tidq->prev_packet->cpu,
> + tidq->prev_packet->flags, from, to, size,
> + etmq->buffer->buffer_nr + 1, true,
> + tidq->br_stack_sz, 0);
> + } else {
> + thread_stack__set_trace_nr(tidq->thread, tidq->prev_packet->cpu,
> + etmq->buffer->buffer_nr + 1);
> + }
> +}
> +
> static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
> struct cs_etm_traceid_queue *tidq,
> u64 addr, u64 period)
> @@ -1608,8 +1552,12 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
>
> cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->packet, &sample);
>
> - if (etm->synth_opts.last_branch)
> + if (etm->synth_opts.last_branch) {
> + thread_stack__br_sample(tidq->thread, tidq->packet->cpu,
> + tidq->last_branch,
> + tidq->br_stack_sz);
> sample.branch_stack = tidq->last_branch;
> + }
>
> if (etm->synth_opts.inject) {
> ret = cs_etm__inject_event(etm, event, &sample,
> @@ -1798,14 +1746,7 @@ static int cs_etm__sample(struct cs_etm_queue *etmq,
>
> tidq->period_instructions += tidq->packet->instr_count;
>
> - /*
> - * Record a branch when the last instruction in
> - * PREV_PACKET is a branch.
> - */
> - if (etm->synth_opts.last_branch &&
> - tidq->prev_packet->sample_type == CS_ETM_RANGE &&
> - tidq->prev_packet->last_instr_taken_branch)
> - cs_etm__update_last_branch_rb(etmq, tidq);
> + cs_etm__add_stack_event(etmq, tidq);
Would it be cleaner to call this whenever a branch sample is generated?
Seems like the conditions for calling thread_stack__event() and
cs_etm__synth_branch_sample() are slightly different (ignoring the fact
that branches are only generated when the user asks for them).
Maybe the conditions should be different, but maybe a comment why or if
they're the same, a shared function for the conditions would help.
For example, we don't push a branch to the stack for
CS_ETM_DISCONTINUITY, but we do generate a branch sample from 0.
>
> if (etm->synth_opts.instructions &&
> tidq->period_instructions >= etm->instructions_sample_period) {
> @@ -1864,10 +1805,6 @@ static int cs_etm__sample(struct cs_etm_queue *etmq,
> u64 offset = etm->instructions_sample_period - instrs_prev;
> u64 addr;
>
> - /* Prepare last branches for instruction sample */
> - if (etm->synth_opts.last_branch)
> - cs_etm__copy_last_branch_rb(etmq, tidq);
> -
> while (tidq->period_instructions >=
> etm->instructions_sample_period) {
> /*
> @@ -1947,10 +1884,6 @@ static int cs_etm__flush(struct cs_etm_queue *etmq,
> etmq->etm->synth_opts.instructions &&
> tidq->prev_packet->sample_type == CS_ETM_RANGE) {
> u64 addr;
> -
> - /* Prepare last branches for instruction sample */
> - cs_etm__copy_last_branch_rb(etmq, tidq);
> -
> /*
> * Generate a last branch event for the branches left in the
> * circular buffer at the end of the trace.
> @@ -1982,7 +1915,7 @@ static int cs_etm__flush(struct cs_etm_queue *etmq,
>
> /* Reset last branches after flush the trace */
> if (etm->synth_opts.last_branch)
> - cs_etm__reset_last_branch_rb(tidq);
> + thread_stack__flush(tidq->thread);
>
> return err;
> }
> @@ -2006,9 +1939,6 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq,
> tidq->prev_packet->sample_type == CS_ETM_RANGE) {
> u64 addr;
>
> - /* Prepare last branches for instruction sample */
> - cs_etm__copy_last_branch_rb(etmq, tidq);
> -
> /*
> * Use the address of the end of the last reported execution
> * range.
>
^ permalink raw reply
* Re: [PATCH v6 1/8] perf cs-etm: Decode ETE exception packets
From: James Clark @ 2026-06-04 14:10 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-1-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> ETE shares the same packet format as ETMv4, but exception decoding
> handled ETMv4 packets only. As a result, ETE exception packets were
> not classified.
>
> Recognize the ETE magic for exception number decoding.
>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index 6ec48de29441012f3d827d50616349c6c0d1f037..ab79d08f5a6095448470e2c3ec85ff3db2fb5634 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -2138,7 +2138,7 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq,
> * HVC cases; need to check if it's SVC instruction based on
> * packet address.
> */
> - if (magic == __perf_cs_etmv4_magic) {
> + if (magic == __perf_cs_etmv4_magic || magic == __perf_cs_ete_magic) {
> if (packet->exception_number == CS_ETMV4_EXC_CALL &&
> cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet,
> prev_packet->end_addr))
> @@ -2161,7 +2161,7 @@ static bool cs_etm__is_async_exception(struct cs_etm_traceid_queue *tidq,
> packet->exception_number == CS_ETMV3_EXC_FIQ)
> return true;
>
> - if (magic == __perf_cs_etmv4_magic)
> + if (magic == __perf_cs_etmv4_magic || magic == __perf_cs_ete_magic)
> if (packet->exception_number == CS_ETMV4_EXC_RESET ||
> packet->exception_number == CS_ETMV4_EXC_DEBUG_HALT ||
> packet->exception_number == CS_ETMV4_EXC_SYSTEM_ERROR ||
> @@ -2192,7 +2192,7 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq,
> packet->exception_number == CS_ETMV3_EXC_GENERIC)
> return true;
>
> - if (magic == __perf_cs_etmv4_magic) {
> + if (magic == __perf_cs_etmv4_magic || magic == __perf_cs_ete_magic) {
> if (packet->exception_number == CS_ETMV4_EXC_TRAP ||
> packet->exception_number == CS_ETMV4_EXC_ALIGNMENT ||
> packet->exception_number == CS_ETMV4_EXC_INST_FAULT ||
>
Reviewed-by: James Clark <james.clark@linaro.org>
^ permalink raw reply
* Re: [PATCH v6 2/8] perf cs-etm: Refactor instruction size handling
From: James Clark @ 2026-06-04 14:11 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users, Leo Yan,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-2-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> From: Leo Yan <leo.yan@linaro.org>
>
> This patch introduces a new function cs_etm__instr_size() to calculate
> the instruction size based on ISA type and instruction address.
>
> Given the trace data can be MB and most likely that will be A64/A32 on
> a lot of platforms, cs_etm__instr_addr() keeps a single ISA type check
> for A64/A32 and executes an optimized calculation (addr + offset * 4).
>
> Signed-off-by: Leo Yan <leo.yan@linaro.org>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 44 +++++++++++++++++++++++---------------------
> 1 file changed, 23 insertions(+), 21 deletions(-)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index ab79d08f5a6095448470e2c3ec85ff3db2fb5634..5bff8811d61e423463b7bd4e20d599d5b5307a1a 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -1347,6 +1347,17 @@ static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq,
> return ((instrBytes[1] & 0xF8) >= 0xE8) ? 4 : 2;
> }
>
> +static inline int cs_etm__instr_size(struct cs_etm_queue *etmq,
> + u8 trace_chan_id,
> + enum cs_etm_isa isa, u64 addr)
> +{
> + if (isa == CS_ETM_ISA_T32)
> + return cs_etm__t32_instr_size(etmq, trace_chan_id, addr);
> +
> + /* Otherwise, 4-byte instruction size for A32/A64 */
> + return 4;
> +}
> +
> static inline u64 cs_etm__first_executed_instr(struct cs_etm_packet *packet)
> {
> /*
> @@ -1375,19 +1386,18 @@ static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq,
> const struct cs_etm_packet *packet,
> u64 offset)
> {
> - if (packet->isa == CS_ETM_ISA_T32) {
> - u64 addr = packet->start_addr;
> + u64 addr = packet->start_addr;
>
> - while (offset) {
> - addr += cs_etm__t32_instr_size(etmq,
> - trace_chan_id, addr);
> - offset--;
> - }
> - return addr;
> - }
> + /* 4-byte instruction size for A32/A64 */
> + if (packet->isa == CS_ETM_ISA_A64 || packet->isa == CS_ETM_ISA_A32)
> + return addr + offset * 4;
>
> - /* Assume a 4 byte instruction size (A32/A64) */
> - return packet->start_addr + offset * 4;
> + while (offset) {
> + addr += cs_etm__instr_size(etmq, trace_chan_id,
> + packet->isa, addr);
> + offset--;
> + }
> + return addr;
> }
>
> static void cs_etm__update_last_branch_rb(struct cs_etm_queue *etmq,
> @@ -1540,16 +1550,8 @@ static void cs_etm__copy_insn(struct cs_etm_queue *etmq,
> return;
> }
>
> - /*
> - * T32 instruction size might be 32-bit or 16-bit, decide by calling
> - * cs_etm__t32_instr_size().
> - */
> - if (packet->isa == CS_ETM_ISA_T32)
> - sample->insn_len = cs_etm__t32_instr_size(etmq, trace_chan_id,
> - sample->ip);
> - /* Otherwise, A64 and A32 instruction size are always 32-bit. */
> - else
> - sample->insn_len = 4;
> + sample->insn_len = cs_etm__instr_size(etmq, trace_chan_id,
> + packet->isa, sample->ip);
>
> cs_etm__mem_access(etmq, trace_chan_id, sample->ip, sample->insn_len,
> (void *)sample->insn, 0);
>
Reviewed-by: James Clark <james.clark@linaro.org>
^ permalink raw reply
* Re: [PATCH v6 4/8] perf cs-etm: Flush thread stacks after decoder reset
From: James Clark @ 2026-06-04 14:12 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-4-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> Perf resets the CoreSight decoder when moving to a new AUX trace buffer,
> this causes trace discontinunity globally.
>
> For callchain synthesis, keeping thread-stack state after decoder reset
> can leave stale call/return history attached to threads that are decoded
> later, producing incorrect synthesized callchains.
>
> Flush all host thread stacks after a decoder reset. When virtualization
> is present, flush the guest thread stacks as well.
>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 37 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 37 insertions(+)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index 398ab3b7a429d402cc8e5f6cccb35c0b7c253732..ea2424175558ddc0a6f20a9de6c30f377facdc52 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -1956,6 +1956,37 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq,
>
> return 0;
> }
> +
> +static int cs_etm__flush_stack_cb(struct thread *thread,
> + void *data __maybe_unused)
> +{
> + thread_stack__flush(thread);
> + return 0;
> +}
> +
> +static void cs_etm__flush_machine_stack(struct cs_etm_queue *etmq, pid_t pid)
> +{
> + struct machine *machine;
> +
> + machine = machines__find(&etmq->etm->session->machines, pid);
> + if (machine)
> + machine__for_each_thread(machine, cs_etm__flush_stack_cb, NULL);
> +}
> +
> +static void cs_etm__flush_all_stack(struct cs_etm_queue *etmq)
> +{
> + enum cs_etm_pid_fmt pid_fmt = cs_etm__get_pid_fmt(etmq);
> +
> + if (!etmq->etm->synth_opts.last_branch)
> + return;
> +
> + cs_etm__flush_machine_stack(etmq, HOST_KERNEL_ID);
> +
> + /* Clear the guest stack if virtualization is supported */
> + if (pid_fmt == CS_ETM_PIDFMT_CTXTID2)
> + cs_etm__flush_machine_stack(etmq, DEFAULT_GUEST_KERNEL_ID);
> +}
> +
> /*
> * cs_etm__get_data_block: Fetch a block from the auxtrace_buffer queue
> * if need be.
> @@ -1978,6 +2009,12 @@ static int cs_etm__get_data_block(struct cs_etm_queue *etmq)
> ret = cs_etm_decoder__reset(etmq->decoder);
> if (ret)
> return ret;
> +
> + /*
> + * Since the decoder is reset, this causes a global trace
> + * discontinuity. Flush all thread stacks.
> + */
> + cs_etm__flush_all_stack(etmq);
> }
>
> return etmq->buf_len;
>
Reviewed-by: James Clark <james.clark@linaro.org>
^ permalink raw reply
* Re: [PATCH v3 05/32] iommu/generic_pt: implement iova_to_phys_length
From: Jason Gunthorpe @ 2026-06-04 14:12 UTC (permalink / raw)
To: Baolu Lu
Cc: Guanghui Feng, adrian.larumbe, airlied, alex, alikernel-developer,
boris.brezillon, dri-devel, dwmw2, iommu, joro, kevin.tian, kvm,
linux-arm-kernel, linux-kernel, liviu.dudau, maarten.lankhorst,
mripard, oliver.yang, robh, robin.murphy, shiyu.zsq, steven.price,
suravee.suthikulpanit, tzimmermann, wei.guo.simon, will, xlpang
In-Reply-To: <c179b999-8c7f-4338-8aad-b61fd09f1329@linux.intel.com>
On Thu, Jun 04, 2026 at 11:30:37AM +0800, Baolu Lu wrote:
> > -static __always_inline int __do_iova_to_phys(struct pt_range *range, void *arg,
> > - unsigned int level,
> > - struct pt_table_p *table,
> > - pt_level_fn_t descend_fn)
> > +struct iova_to_phys_length_data {
> > + pt_oaddr_t phys;
> > + size_t length;
> > +};
> > +
> > +static __always_inline int __do_iova_to_phys_length(struct pt_range *range,
> > + void *arg, unsigned int level,
> > + struct pt_table_p *table,
> > + pt_level_fn_t descend_fn)
> > {
> > struct pt_state pts = pt_init(range, level, table);
> > - pt_oaddr_t *res = arg;
> > + struct iova_to_phys_length_data *data = arg;
> > + unsigned int entry_lg2sz;
> > + size_t entry_sz;
> > + pt_oaddr_t expected_oa;
> > switch (pt_load_single_entry(&pts)) {
> > case PT_ENTRY_EMPTY:
> > @@ -159,45 +167,77 @@ static __always_inline int __do_iova_to_phys(struct pt_range *range, void *arg,
> > case PT_ENTRY_TABLE:
> > return pt_descend(&pts, arg, descend_fn);
> > case PT_ENTRY_OA:
> > - *res = pt_entry_oa_exact(&pts);
> > - return 0;
> > + break;
> > }
> > - return -ENOENT;
> > +
> > + data->phys = pt_entry_oa_exact(&pts);
> > + entry_lg2sz = pt_entry_oa_lg2sz(&pts);
> > + entry_sz = log2_to_int(entry_lg2sz);
> > +
> > + /* Start with the full mapping size of the first entry */
> > + data->length = entry_sz;
>
> data->length doesn't account for iova offset. Is this by design? We
> should document this clearly somewhere.
That's defintaely a mistake, the phys has to be offset by the iova in all cases,
it is part of the API.
Also add kunits tests to the iommupt selftest to cover various
scenarios please.
Also this doesn't look quite right, the walk should look more like
unmap where we just walk and stop walking when we hit a physical
address discontiguity. The stop point defines the result length.
Jason
^ permalink raw reply
* Re: [PATCH v3 01/32] iommu: introduce iova_to_phys_length in iommu_domain_ops
From: Jason Gunthorpe @ 2026-06-04 14:16 UTC (permalink / raw)
To: Guanghui Feng
Cc: adrian.larumbe, airlied, alex, alikernel-developer, baolu.lu,
boris.brezillon, dri-devel, dwmw2, iommu, joro, kevin.tian, kvm,
linux-arm-kernel, linux-kernel, liviu.dudau, maarten.lankhorst,
mripard, oliver.yang, robh, robin.murphy, shiyu.zsq, steven.price,
suravee.suthikulpanit, tzimmermann, wei.guo.simon, will, xlpang
In-Reply-To: <20260603151804.1963871-2-guanghuifeng@linux.alibaba.com>
On Wed, Jun 03, 2026 at 11:17:33PM +0800, Guanghui Feng wrote:
> +phys_addr_t iommu_iova_to_phys_length(struct iommu_domain *domain,
> + dma_addr_t iova,
> + size_t *mapped_length)
> {
This should take in an ending point so the accumulation knows when to
stop, otherwise it is too hard to use.
> - if (domain->type == IOMMU_DOMAIN_IDENTITY)
> + phys_addr_t phys;
> +
> + if (domain->type == IOMMU_DOMAIN_IDENTITY) {
> + if (mapped_length)
> + *mapped_length = PAGE_SIZE;
> return iova;
> + }
>
> - if (domain->type == IOMMU_DOMAIN_BLOCKED)
> - return 0;
> + if (mapped_length)
> + *mapped_length = 0;
> +
> + if (domain->ops->iova_to_phys_length)
> + return domain->ops->iova_to_phys_length(domain, iova, mapped_length);
> +
> + /* Fallback to legacy iova_to_phys without length info */
> + if (!domain->ops->iova_to_phys)
> + return PHYS_ADDR_MAX;
> +
> + phys = domain->ops->iova_to_phys(domain, iova);
> + if (!phys)
> + return PHYS_ADDR_MAX;
And to properly clean up the callers all the non-iommupt paths should
manually do accumulation here as well.
Basically if you call this function you get a maximal contiguous
physical range as efficiently as possible.
Jason
^ permalink raw reply
* Re: [RFC PATCH v3 6/9] iommu/rockchip: Clear AUTO_GATING bit 1 on the RK356x v1 IOMMU
From: Tomeu Vizoso @ 2026-06-04 14:20 UTC (permalink / raw)
To: Midgy BALON
Cc: ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will, robin.murphy,
dri-devel, linux-rockchip, devicetree, linux-arm-kernel, iommu,
linux-kernel
In-Reply-To: <20260604135255.62682-7-midgy971@gmail.com>
On Thu, Jun 4, 2026 at 3:53 PM Midgy BALON <midgy971@gmail.com> wrote:
>
> On the RK356x v1 IOMMU, RK_MMU_AUTO_GATING resets to 0x3. Bit 1 enables
> auto clock-gating of the page-table walker, so the walker's AXI master
> loses its clock between transactions; a TLB-miss page walk then never
> completes and the IOMMU is left stuck (PAGING_ENABLED, never IDLE).
>
> Clear bit 1 (keeping bit 0, the slave-port gate) once paging is enabled
> so the walker keeps its clock. This is required for the RK3568 NPU MMU.
Hi,
I'm not able to review this patch myself, but maybe it can be
submitted separately while we work on the NPU bits?
Regards,
Tomeu
> Signed-off-by: Midgy BALON <midgy971@gmail.com>
> ---
> drivers/iommu/rockchip-iommu.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index 4da80136933c4..e3d8b6e9ca12b 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -953,6 +953,18 @@ static int rk_iommu_enable(struct rk_iommu *iommu)
>
> ret = rk_iommu_enable_paging(iommu);
>
> + if (!ret) {
> + /*
> + * RK356x v1 IOMMU: RK_MMU_AUTO_GATING bit 1 enables page-walker
> + * auto clock-gating; the walker's AXI master then loses its clock
> + * between transactions and a TLB-miss page walk never completes,
> + * leaving the IOMMU stuck (PAGING_ENABLED, never IDLE). Clear
> + * bit 1 (keep bit 0, the slave-port gate) once paging is enabled.
> + */
> + for (i = 0; i < iommu->num_mmu; i++)
> + rk_iommu_write(iommu->bases[i], RK_MMU_AUTO_GATING, 0x2);
> + }
> +
> out_disable_stall:
> rk_iommu_disable_stall(iommu);
> out_disable_clocks:
> --
> 2.39.5
>
^ permalink raw reply
* Re: [PATCH v6 5/8] perf cs-etm: Support call indentation
From: James Clark @ 2026-06-04 14:24 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users, Leo Yan,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-5-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> From: Leo Yan <leo.yan@linaro.org>
>
> This commit supports the field "callindent" to reflect the call stack
> depth.
>
> The branch stack is used by both call indentation and the last branch
> record, which are separate features. Use a new flag "use_br_stack" to
> track whether the branch stack needs to be recorded.
>
> Before:
>
> perf script -F +callindent
>
> callchain_test 9187 [002] 599611.826599: 1 branches: main ffff83312258 __libc_start_call_main+0x78 (/usr/lib/aarch64-linux-gnu/libc.so.6)
> callchain_test 9187 [002] 599611.826599: 1 branches: foo aaaae3ed07c4 main+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: print aaaae3ed07ac foo+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: do_svc aaaae3ed0794 print+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: aaaae3ed077c do_svc+0x14 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: vectors aaaae3ed0780 do_svc+0x18 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: ffff800080010c00 vectors+0x400 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080010c24 vectors+0x424 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff8000800114dc el0t_64_sync+0xd4 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff8000800114f8 el0t_64_sync+0xf0 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080011528 el0t_64_sync+0x120 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080011538 el0t_64_sync+0x130 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: ffff800080011568 el0t_64_sync+0x160 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff80008001159c el0t_64_sync+0x194 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: ffff800081829110 el0t_64_sync_handler+0x18 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff800081829140 el0t_64_sync_handler+0x48 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0_svc ffff800081829194 el0t_64_sync_handler+0x9c ([kernel.kallsyms])
>
> After:
>
> callchain_test 9187 [002] 599611.826599: 1 branches: main ffff83312258 __libc_start_call_main+0x78 (/usr/lib/aarch64-linux-gnu/libc.so.6)
> callchain_test 9187 [002] 599611.826599: 1 branches: foo aaaae3ed07c4 main+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: print aaaae3ed07ac foo+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: do_svc aaaae3ed0794 print+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: aaaae3ed077c do_svc+0x14 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: vectors aaaae3ed0780 do_svc+0x18 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: ffff800080010c00 vectors+0x400 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080010c24 vectors+0x424 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff8000800114dc el0t_64_sync+0xd4 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff8000800114f8 el0t_64_sync+0xf0 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080011528 el0t_64_sync+0x120 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080011538 el0t_64_sync+0x130 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: ffff800080011568 el0t_64_sync+0x160 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff80008001159c el0t_64_sync+0x194 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: ffff800081829110 el0t_64_sync_handler+0x18 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff800081829140 el0t_64_sync_handler+0x48 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0_svc ffff800081829194 el0t_64_sync_handler+0x9c ([kernel.kallsyms])
>
> Signed-off-by: Leo Yan <leo.yan@linaro.org>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 14 ++++++++++----
> 1 file changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index ea2424175558ddc0a6f20a9de6c30f377facdc52..b31d0dd46a45dc365edd7c2f9e9b2eb077ca23db 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -66,6 +66,7 @@ struct cs_etm_auxtrace {
> bool snapshot_mode;
> bool data_queued;
> bool has_virtual_ts; /* Virtual/Kernel timestamps in the trace. */
> + bool use_thread_stack;
>
> int num_cpu;
> u64 latest_kernel_timestamp;
> @@ -626,7 +627,7 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
> if (!tidq->prev_packet)
> goto out_free;
>
> - if (etm->synth_opts.last_branch) {
> + if (etm->use_thread_stack) {
> size_t sz = sizeof(struct branch_stack);
>
> sz += etm->synth_opts.last_branch_sz *
> @@ -1505,7 +1506,7 @@ static void cs_etm__add_stack_event(struct cs_etm_queue *etmq,
> tidq->packet->sample_type != CS_ETM_RANGE)
> return;
>
> - if (etmq->etm->synth_opts.last_branch) {
> + if (etmq->etm->use_thread_stack) {
> from = cs_etm__last_executed_instr(tidq->prev_packet);
> to = cs_etm__first_executed_instr(tidq->packet);
>
> @@ -1914,7 +1915,7 @@ static int cs_etm__flush(struct cs_etm_queue *etmq,
> cs_etm__packet_swap(etm, tidq);
>
> /* Reset last branches after flush the trace */
> - if (etm->synth_opts.last_branch)
> + if (etm->use_thread_stack)
> thread_stack__flush(tidq->thread);
>
> return err;
> @@ -1977,7 +1978,7 @@ static void cs_etm__flush_all_stack(struct cs_etm_queue *etmq)
> {
> enum cs_etm_pid_fmt pid_fmt = cs_etm__get_pid_fmt(etmq);
>
> - if (!etmq->etm->synth_opts.last_branch)
> + if (!etmq->etm->use_thread_stack)
> return;
>
> cs_etm__flush_machine_stack(etmq, HOST_KERNEL_ID);
> @@ -3438,6 +3439,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
> itrace_synth_opts__set_default(&etm->synth_opts,
> session->itrace_synth_opts->default_no_sample);
> etm->synth_opts.callchain = false;
> + etm->synth_opts.thread_stack = session->itrace_synth_opts->thread_stack;
> }
>
> etm->session = session;
> @@ -3489,6 +3491,10 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
> etm->tc.cap_user_time_zero = tc->cap_user_time_zero;
> etm->tc.cap_user_time_short = tc->cap_user_time_short;
> }
> +
> + etm->use_thread_stack = etm->synth_opts.thread_stack ||
> + etm->synth_opts.last_branch;
> +
> err = cs_etm__synth_events(etm, session);
> if (err)
> goto err_free_queues;
>
Reviewed-by: James Clark <james.clark@linaro.org>
^ permalink raw reply
* Re: [PATCH v3 24/32] iommufd: use iova_to_phys_length for efficient unmap
From: Jason Gunthorpe @ 2026-06-04 14:26 UTC (permalink / raw)
To: Guanghui Feng
Cc: adrian.larumbe, airlied, alex, alikernel-developer, baolu.lu,
boris.brezillon, dri-devel, dwmw2, iommu, joro, kevin.tian, kvm,
linux-arm-kernel, linux-kernel, liviu.dudau, maarten.lankhorst,
mripard, oliver.yang, robh, robin.murphy, shiyu.zsq, steven.price,
suravee.suthikulpanit, tzimmermann, wei.guo.simon, will, xlpang
In-Reply-To: <20260603151804.1963871-25-guanghuifeng@linux.alibaba.com>
On Wed, Jun 03, 2026 at 11:17:56PM +0800, Guanghui Feng wrote:
> Use iommu_iova_to_phys_length() to get PTE page size in
> + for (i = 0; i < npages; i++) {
> + if (!batch_add_pfn(batch, PHYS_PFN(phys) + i))
> + return;
batch_add_pfn_num()
Be mindful that the num is purposfully a u32 so that will need some
attention.
Jason
^ permalink raw reply
* Re: [PATCH v3 23/32] vfio: use iova_to_phys_length for efficient unmap
From: Jason Gunthorpe @ 2026-06-04 14:27 UTC (permalink / raw)
To: Guanghui Feng
Cc: adrian.larumbe, airlied, alex, alikernel-developer, baolu.lu,
boris.brezillon, dri-devel, dwmw2, iommu, joro, kevin.tian, kvm,
linux-arm-kernel, linux-kernel, liviu.dudau, maarten.lankhorst,
mripard, oliver.yang, robh, robin.murphy, shiyu.zsq, steven.price,
suravee.suthikulpanit, tzimmermann, wei.guo.simon, will, xlpang
In-Reply-To: <20260603151804.1963871-24-guanghuifeng@linux.alibaba.com>
On Wed, Jun 03, 2026 at 11:17:55PM +0800, Guanghui Feng wrote:
> Use iommu_iova_to_phys_length() to get PTE page size, allowing
> traversal by actual mapping granularity instead of PAGE_SIZE steps.
>
> Signed-off-by: Guanghui Feng <guanghuifeng@linux.alibaba.com>
> Acked-by: Shiqiang Zhang <shiyu.zsq@linux.alibaba.com>
> Acked-by: Simon Guo <wei.guo.simon@linux.alibaba.com>
> ---
> drivers/vfio/vfio_iommu_type1.c | 27 ++++++++++++++++++++++-----
> 1 file changed, 22 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
> index c8151ba54de3..115d88d7003e 100644
> --- a/drivers/vfio/vfio_iommu_type1.c
> +++ b/drivers/vfio/vfio_iommu_type1.c
> @@ -1177,25 +1177,42 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
>
> iommu_iotlb_gather_init(&iotlb_gather);
> while (pos < dma->size) {
> - size_t unmapped, len;
> + size_t unmapped, len, pgsize;
> phys_addr_t phys, next;
> dma_addr_t iova = dma->iova + pos;
>
> - phys = iommu_iova_to_phys(domain->domain, iova);
> - if (WARN_ON(!phys)) {
> + /* Single page table walk returns both phys and PTE size */
> + phys = iommu_iova_to_phys_length(domain->domain, iova,
> + &pgsize);
> + if (WARN_ON(phys == PHYS_ADDR_MAX)) {
> pos += PAGE_SIZE;
> continue;
> }
> + if (WARN_ON(!pgsize || pgsize < PAGE_SIZE))
> + pgsize = PAGE_SIZE;
>
> /*
> * To optimize for fewer iommu_unmap() calls, each of which
> * may require hardware cache flushing, try to find the
> * largest contiguous physical memory chunk to unmap.
> + *
> + * mapped_length already accounts for contiguous entries
> + * from iova, then try to join following physically
> + * contiguous PTEs.
> */
> - for (len = PAGE_SIZE; pos + len < dma->size; len += PAGE_SIZE) {
> - next = iommu_iova_to_phys(domain->domain, iova + len);
> + len = min_t(size_t, pgsize, dma->size - pos);
> + for (; pos + len < dma->size; ) {
> + size_t next_pgsize;
> +
> + next = iommu_iova_to_phys_length(domain->domain,
> + iova + len,
> + &next_pgsize);
vfio should not be calling it twice, the core code needs to give the
best length as efficiently as it can. not open coding this in callers.
I think I've said this three times now
Jason
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox