Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] mailbox: bcm2835: Add ACPI support for UEFI-booted Raspberry Pi 4
From: Steve Crawford @ 2026-06-07 15:43 UTC (permalink / raw)
  To: jassisinghbrar
  Cc: linux-kernel, linux-arm-kernel, kernel-list, Steve Crawford

On Raspberry Pi 4 booted via UEFI (e.g. using the pftf/RPi4 firmware),
the kernel enumerates devices via ACPI rather than the device tree. The
RPIQ mailbox device is already correctly described in the edk2-platforms
GpuDevs.asl with HID BCM2849, complete with MMIO and interrupt resources,
but the driver has no ACPI match table and therefore never binds.

Add an ACPI device ID table matching BCM2849, and replace the
DT-only irq_of_parse_and_map() call with platform_get_irq(), which
handles both DT and ACPI resource lookup transparently.

The existing DT path (brcm,bcm2835-mbox) is unchanged; this is a
purely additive change that has no effect on any boot path other than
UEFI+ACPI on RPi4.

Testing
-------
On Fedora CoreOS 44 (kernel 7.0.8-200.fc44.aarch64) booted via pftf/RPi4
UEFI v1.42, BCM2849:00 (the RPIQ mailbox device) is visible in ACPI but
unbound. Using driver_override to force-bind the unpatched driver
reproduces the exact failure this patch fixes:

  # echo bcm2835-mbox > /sys/bus/platform/devices/BCM2849:00/driver_override
  # echo BCM2849:00 > /sys/bus/platform/drivers/bcm2835-mbox/bind
  bcm2835-mbox BCM2849:00: error -EINVAL: request_irq(0) bcm2835_mbox_irq 0x0 BCM2849:00
  bcm2835-mbox BCM2849:00: Failed to register a mailbox IRQ handler: -22

irq_of_parse_and_map() returns 0 when dev->of_node is NULL (ACPI case).
platform_get_irq() correctly reads the interrupt from the ACPI _CRS
resources instead.

Signed-off-by: Steve Crawford <steve@crawford.dev>
---
 bcm2835-mailbox.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/bcm2835-mailbox.c b/bcm2835-mailbox.c
index ea12fb8..d450f3b 100644
--- a/bcm2835-mailbox.c
+++ b/bcm2835-mailbox.c
@@ -15,6 +15,7 @@
  *    https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
  */
 
+#include <linux/acpi.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
 #include <linux/err.h>
@@ -24,7 +25,6 @@
 #include <linux/mailbox_controller.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
-#include <linux/of_irq.h>
 #include <linux/platform_device.h>
 #include <linux/spinlock.h>
 
@@ -144,7 +144,7 @@ static int bcm2835_mbox_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	spin_lock_init(&mbox->lock);
 
-	ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),
+	ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
 			       bcm2835_mbox_irq, IRQF_NO_SUSPEND, dev_name(dev),
 			       mbox);
 	if (ret) {
@@ -186,10 +186,24 @@ static const struct of_device_id bcm2835_mbox_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, bcm2835_mbox_of_match);
 
+static const struct acpi_device_id bcm2835_mbox_acpi_match[] = {
+	/*
+	 * BCM2849 is the ACPI HID for the RPi4 UEFI firmware's RPIQ mailbox
+	 * device (defined in GpuDevs.asl of the tianocore/edk2-platforms RPi4
+	 * platform). On UEFI-booted systems this device carries the correct
+	 * MMIO and interrupt resources, allowing the driver to bind via ACPI
+	 * rather than the device tree.
+	 */
+	{ "BCM2849", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, bcm2835_mbox_acpi_match);
+
 static struct platform_driver bcm2835_mbox_driver = {
 	.driver = {
 		.name = "bcm2835-mbox",
 		.of_match_table = bcm2835_mbox_of_match,
+		.acpi_match_table = bcm2835_mbox_acpi_match,
 	},
 	.probe		= bcm2835_mbox_probe,
 };
-- 
2.54.0



^ permalink raw reply related

* Re: [PATCH v3 0/5] remoteproc: cleanup shared carveout and resource-table helpers
From: Wolfram Sang @ 2026-06-07 14:57 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: Ben Levinsky, arnaud.pouliquen, daniel.baluta, peng.fan,
	Bjorn Andersson, linux-remoteproc, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Geert Uytterhoeven,
	Magnus Damm, Patrice Chotard, Maxime Coquelin, Alexandre Torgue,
	Tanmay Shah, imx, linux-arm-kernel, linux-kernel,
	linux-renesas-soc, linux-stm32
In-Reply-To: <ah2aVdlsLqy9aeHP@p14s>


> Wolfram has already indicated he wanted to test these changes - Arnaud, Daniel
> and Peng, please do the same for your platforms.

Testing is more involved than I expected. Didn't work out this week.
I'll keep trying for next week. But it may end up the week after that.



^ permalink raw reply

* [PATCH v3 1/1] crypto: atmel-ecc - fix multi-device use-after-free and registration races
From: Lothar Rubusch @ 2026-06-07 14:18 UTC (permalink / raw)
  To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
	claudiu.beznea, tudor.ambarus, krzk+dt
  Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch

During parallel driver initialization or driver teardown sequences
in setups with multiple atmel-ecc instances, a race condition exists
between atmel_ecc_i2c_client_alloc() and the probe/remove paths.

A concurrent transformation request can fetch an i2c_client instance
from the global i2c_client_list before the kpp is fully registered, or
while it is actively being unbound, resulting in a use-after-free (UAF)
risk.

1. The initialization problem in probe(): Adding first an i2c client to the
i2c_client_list, and then registering the kpp algorim may result in a race,
when this happens for a second (or further) probed device. In this case the
algorithm is already registered, so a TFM may arrive, while the latest
probing device is added to the list, but not kpp registered. In case this
fails and this last device is going to be removed again from the list, this
leaves a window where the TFM might obtain a pointer to the - now deleted -
i2c client, which opens a UAF risk. Furthermore, there will happen atempts
to multiple registering the same driver to the same type of algorithm.
Note, a simple reverting of the order: first register kpp, second add the
i2c client to the i2c_client_list - is not possible here, since the kpp
registration immediately triggers the self tests, which then will allocate
and require an i2c client.

2. The critical race condition problem: It exists when an Atmel device
instance is rapidly removed and immediately re-probed, before the global
resources are fully cleaned up. In this scenario, the asynchronous
unregistration sequence in the remove() lags behind the incoming probe()
function. Because of the global algorithm structure being not yet
completely cleaned up, the newly re-probed device incorrectly intercepts
the static, partially-dismantled global context. It then overwrites active
pointers and re-acquires the global instance prematurely. In this way, when
the deregistration sequence finally completes its execution, under the
newly initialized device, it may lose the tracking references, leaking the
older driver memory blocks, and introducing an immediate UAF risk.

3. The removal race problem, when a call to remove() starts removing the
device, but another thread executing a TFM, a severe Time-of-Check to
Time-of-Use (TOCTOU) race condition exists in the teardown path between the
asynchronous remove() sequence and completing TFMs. When the device is
unbound, the remove() function evaluates the active tfm_count and decides
whether to wait or proceed with resource deallocation. However, if the
final active TFM finishes its crypto operation and invokes the client free
function immediately after remove() performs its reference check but before
it can sleep, the completion signal is fired into a clearing state. The
unbind thread then misinterprets the zeroed counter, skips the
synchronization barrier entirely, and instantly deallocates the per-device
private structures. This leaves the final TFM worker thread executing code
inside a completely freed memory area, triggering an immediate UAF kernel
panic. Note, simply calling the kpp unregister here won't clean up the
situation in the context of having a setup with external hardware on a slow
bus.

Address this by implementing an independent subsystem reference counter
kpp refcnt protected by a dedicated mutex to ensure the static global kpp
algorithm structure is registered exactly once by the first probing device
instance. In multi-device scenarios, or when extending the resource
management support of the i2c_client_list to all atmel-i2c based device
drivers, such scenarios can become realistic. The particular algorithm is
registered only once. Each i2c client (i.e. each probing device driver) is
added as client to the i2c_client_list. This guarantee that only the first
probe will register the algorithm. The list is populated for further calls
to probe, and subsequent calls to the client alloc function.

Concurrently, decouple list mutations from registration by moving the
global list eviction to the absolute top of the remove lifecycle. This
keeps the quick execution of the list allocation loop intact, ensures that
unbinding hardware is instantly blind to the rest of the system, and
completely bypasses the recursive deadlock condition previously triggered
by synchronous crypto API self-tests.

Fixes: 11105693fa05 ("crypto: atmel-ecc - introduce Microchip / Atmel ECC driver")
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
 drivers/crypto/atmel-ecc.c | 106 +++++++++++++++++++++++++++----------
 drivers/crypto/atmel-i2c.h |   3 ++
 2 files changed, 82 insertions(+), 27 deletions(-)

diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 0ca02995a1de..df285ca9a6f3 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -23,6 +23,11 @@
 #include <crypto/kpp.h>
 #include "atmel-i2c.h"
 
+static DEFINE_MUTEX(atmel_ecc_kpp_lock);
+static int atmel_ecc_kpp_refcnt;
+DECLARE_COMPLETION(atmel_ecc_unreg_done);
+static bool atmel_ecc_unreg_active;
+
 static struct atmel_ecc_driver_data driver_data;
 
 /**
@@ -241,7 +246,10 @@ static void atmel_ecc_i2c_client_free(struct i2c_client *client)
 {
 	struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
 
-	atomic_dec(&i2c_priv->tfm_count);
+	spin_lock(&driver_data.i2c_list_lock);
+	if (atomic_dec_and_test(&i2c_priv->tfm_count) && i2c_priv->unbinding)
+		complete(&i2c_priv->remove_done);
+	spin_unlock(&driver_data.i2c_list_lock);
 }
 
 static int atmel_ecdh_init_tfm(struct crypto_kpp *tfm)
@@ -276,7 +284,8 @@ static void atmel_ecdh_exit_tfm(struct crypto_kpp *tfm)
 	struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);
 
 	kfree(ctx->public_key);
-	crypto_free_kpp(ctx->fallback);
+	if (ctx->fallback)
+		crypto_free_kpp(ctx->fallback);
 	atmel_ecc_i2c_client_free(ctx->client);
 }
 
@@ -315,6 +324,7 @@ static struct kpp_alg atmel_ecdh_nist_p256 = {
 static int atmel_ecc_probe(struct i2c_client *client)
 {
 	struct atmel_i2c_client_priv *i2c_priv;
+	unsigned long timeout;
 	int ret;
 
 	ret = atmel_i2c_probe(client);
@@ -323,49 +333,91 @@ static int atmel_ecc_probe(struct i2c_client *client)
 
 	i2c_priv = i2c_get_clientdata(client);
 
+	init_completion(&i2c_priv->remove_done);
+	i2c_priv->unbinding = false;
+
 	spin_lock(&driver_data.i2c_list_lock);
 	list_add_tail(&i2c_priv->i2c_client_list_node,
 		      &driver_data.i2c_client_list);
 	spin_unlock(&driver_data.i2c_list_lock);
 
-	ret = crypto_register_kpp(&atmel_ecdh_nist_p256);
-	if (ret) {
-		spin_lock(&driver_data.i2c_list_lock);
-		list_del(&i2c_priv->i2c_client_list_node);
-		spin_unlock(&driver_data.i2c_list_lock);
+	mutex_lock(&atmel_ecc_kpp_lock);
+	/*
+	 * For cases where the same/last such device is still in unregistering,
+	 * and now re-registering (refcnt is 0, but completion still exists).
+	 * Safely capture the pointer, drop the lock and sleep until it
+	 * terminates upon completion or retry limit reached.
+	 */
+	while (atmel_ecc_unreg_active) {
+		mutex_unlock(&atmel_ecc_kpp_lock);
+		timeout = wait_for_completion_timeout(&atmel_ecc_unreg_done,
+						      msecs_to_jiffies(2000));
+		mutex_lock(&atmel_ecc_kpp_lock);
+
+		if (timeout == 0) {
+			spin_lock(&driver_data.i2c_list_lock);
+			list_del(&i2c_priv->i2c_client_list_node);
+			spin_unlock(&driver_data.i2c_list_lock);
+			mutex_unlock(&atmel_ecc_kpp_lock);
+
+			dev_err(&client->dev, "probe timed out, former driver instance not fully deregistered\n");
+			return -ETIMEDOUT;
+		}
+	}
 
-		dev_err(&client->dev, "%s alg registration failed\n",
-			atmel_ecdh_nist_p256.base.cra_driver_name);
-	} else {
-		dev_info(&client->dev, "atmel ecc algorithms registered in /proc/crypto\n");
+	if (atmel_ecc_kpp_refcnt == 0) {
+		ret = crypto_register_kpp(&atmel_ecdh_nist_p256);
+		if (ret) {
+			spin_lock(&driver_data.i2c_list_lock);
+			list_del(&i2c_priv->i2c_client_list_node);
+			spin_unlock(&driver_data.i2c_list_lock);
+			mutex_unlock(&atmel_ecc_kpp_lock);
+
+			dev_err(&client->dev, "%s alg registration failed\n",
+				atmel_ecdh_nist_p256.base.cra_driver_name);
+			return ret;
+		}
 	}
+	atmel_ecc_kpp_refcnt++;
+	mutex_unlock(&atmel_ecc_kpp_lock);
 
+	dev_info(&client->dev, "atmel ecc algorithms registered in /proc/crypto\n");
 	return ret;
 }
 
 static void atmel_ecc_remove(struct i2c_client *client)
 {
 	struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
-
-	/* Return EBUSY if i2c client already allocated. */
-	if (atomic_read(&i2c_priv->tfm_count)) {
-		/*
-		 * After we return here, the memory backing the device is freed.
-		 * That happens no matter what the return value of this function
-		 * is because in the Linux device model there is no error
-		 * handling for unbinding a driver.
-		 * If there is still some action pending, it probably involves
-		 * accessing the freed memory.
-		 */
-		dev_emerg(&client->dev, "Device is busy, expect memory corruption.\n");
-		return;
-	}
-
-	crypto_unregister_kpp(&atmel_ecdh_nist_p256);
+	bool trigger_unreg = false;
+	bool wait_needed = false;
 
 	spin_lock(&driver_data.i2c_list_lock);
 	list_del(&i2c_priv->i2c_client_list_node);
+	i2c_priv->unbinding = true;
+	reinit_completion(&i2c_priv->remove_done);
+	if (atomic_read(&i2c_priv->tfm_count) > 0)
+		wait_needed = true;
 	spin_unlock(&driver_data.i2c_list_lock);
+
+	if (wait_needed)
+		wait_for_completion(&i2c_priv->remove_done);
+
+	mutex_lock(&atmel_ecc_kpp_lock);
+	atmel_ecc_kpp_refcnt--;
+	if (atmel_ecc_kpp_refcnt == 0) {
+		trigger_unreg = true;
+		atmel_ecc_unreg_active = true;
+		reinit_completion(&atmel_ecc_unreg_done);
+	}
+	mutex_unlock(&atmel_ecc_kpp_lock);
+
+	if (trigger_unreg) {
+		crypto_unregister_kpp(&atmel_ecdh_nist_p256);
+		mutex_lock(&atmel_ecc_kpp_lock);
+		atmel_ecc_unreg_active = false;
+		complete_all(&atmel_ecc_unreg_done);
+		mutex_unlock(&atmel_ecc_kpp_lock);
+	}
 }
 
 static const struct of_device_id atmel_ecc_dt_ids[] = {
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index 72f04c15682f..8e6617422191 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -129,6 +129,7 @@ struct atmel_ecc_driver_data {
  * @wake_token_sz       : size in bytes of the wake_token
  * @tfm_count           : number of active crypto transformations on i2c client
  * @hwrng               : hold the hardware generated rng
+ * @unbinding           : unbinding handshake
  *
  * Reads and writes from/to the i2c client are sequential. The first byte
  * transmitted to the device is treated as the byte size. Any attempt to send
@@ -145,6 +146,8 @@ struct atmel_i2c_client_priv {
 	size_t wake_token_sz;
 	atomic_t tfm_count ____cacheline_aligned;
 	struct hwrng hwrng;
+	struct completion remove_done;
+	bool unbinding;
 };
 
 /**

base-commit: 5624ea54f3ba5c83d2e5503411a31a8be0278c1e
prerequisite-patch-id: c4d6779c74f5ca16536803c314af99c4346a14e1
prerequisite-patch-id: edd38ca9cc59d9e34c0944e2e7b533cdeb422c81
prerequisite-patch-id: 55f668761eaff284d13ab86085686eae426fc524
prerequisite-patch-id: b96e5855ea503314931ff3184f96af049c16b19f
prerequisite-patch-id: 5a761e93900d75ef5f887324002c6e52ba47558a
prerequisite-patch-id: 77570d656bdcb6a9bfa3c70b730db8f22767a70b
prerequisite-patch-id: 10a90dfb8890f39c19ea4117ffbb7cf3c801dcb7
prerequisite-patch-id: 86c4da8c2119be06fe5d494066523f6b8507abe4
-- 
2.53.0



^ permalink raw reply related

* Re: [PATCH] KVM: arm64: Set a linux errno on SMCCC error in kvm_call_hyp_nvhe()
From: Marc Zyngier @ 2026-06-07 14:01 UTC (permalink / raw)
  To: Will Deacon
  Cc: Vincent Donnefort, oliver.upton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, linux-arm-kernel, kvmarm, kernel-team,
	tabba
In-Reply-To: <aiKxvK8HXCac4SYU@willie-the-truck>

On Fri, 05 Jun 2026 12:23:40 +0100,
Will Deacon <will@kernel.org> wrote:
> 
> On Wed, Jun 03, 2026 at 12:03:12PM +0100, Vincent Donnefort wrote:
> > If the HVC called in kvm_call_hyp_nvhe() fails with an SMCCC error code,
> > we WARN. However, the returned value isn't initialized and the caller
> > might get garbage or 0 which is likely to be interpreted as success.
> > 
> > Set a default -EPERM error value, ensuring all callers get the message
> > when SMCCC calls fail.
> > 
> > Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
> > 
> > ---
> > 
> > I have encountered this issue while working on a follow-up contribution to the
> > hypervisor tracing. In that case it completely crashed the kernel because
> > IS_ERR() failed on that res.a1 value.
> > 
> > Now, if it makes that function more robust, I do not believe it is fixing any
> > existing bug which is why I haven't added a "Fixes:" tag. 
> > 
> > In case we want to stick one, here it is:
> > 
> > Fixes: 054698316d87 ("KVM: arm64: nVHE: Migrate hyp interface to SMCCC")
> > 
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index a49042bfa801..6b8fd494792c 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -1273,13 +1273,14 @@ void kvm_arm_resume_guest(struct kvm *kvm);
> >  #define vcpu_has_run_once(vcpu)	(!!READ_ONCE((vcpu)->pid))
> >  
> >  #ifndef __KVM_NVHE_HYPERVISOR__
> > -#define kvm_call_hyp_nvhe(f, ...)						\
> > +#define kvm_call_hyp_nvhe(f, ...)					\
> >  	({								\
> >  		struct arm_smccc_res res;				\
> >  									\
> >  		arm_smccc_1_1_hvc(KVM_HOST_SMCCC_FUNC(f),		\
> >  				  ##__VA_ARGS__, &res);			\
> > -		WARN_ON(res.a0 != SMCCC_RET_SUCCESS);			\
> > +		if (WARN_ON(res.a0 != SMCCC_RET_SUCCESS))		\
> > +			res.a1 = -EPERM;				\
> >  									\
> >  		res.a1;							\
> >  	})
> 
> Looks like the only error code we return to the host is
> SMCCC_RET_NOT_SUPPORTED, so maybe -EOPNOTSUPP would be more appropriate?

Yes, this is better. I've hacked the patch to reflect this upon
applying it.

Thanks,

	M.

-- 
Jazz isn't dead. It just smells funny.


^ permalink raw reply

* Re: [PATCH] KVM: arm64: nv: Skip vCPUs without a pseudo-TLB in invalidate_vncr_va()
From: Marc Zyngier @ 2026-06-07 13:05 UTC (permalink / raw)
  To: Hyunwoo Kim
  Cc: oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
	catalin.marinas, will, linux-arm-kernel, kvmarm
In-Reply-To: <aiUvSbrWndQeUPc8@v4bel>

On Sun, 07 Jun 2026 09:43:53 +0100,
Hyunwoo Kim <imv4bel@gmail.com> wrote:
> 
> vncr_tlb is not allocated before a vCPU runs for the first time, so
> vcpu->arch.vncr_tlb is NULL for a vCPU that has been created but not yet
> run. Code that iterates over every vCPU's pseudo-TLB must skip those.
> 
> invalidate_vncr_va() iterates over the vCPUs with kvm_for_each_vcpu() and
> dereferences vt->valid without checking whether vncr_tlb is NULL.
> 
> While iterating, skip vCPUs whose pseudo-TLB has not been allocated.
>
> Fixes: 4ffa72ad8f37 ("KVM: arm64: nv: Add S1 TLB invalidation primitive for VNCR_EL2")
> Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
> ---
>  arch/arm64/kvm/nested.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> index 6f7bc9a9992e..063e079d1d1a 100644
> --- a/arch/arm64/kvm/nested.c
> +++ b/arch/arm64/kvm/nested.c
> @@ -969,6 +969,10 @@ static void invalidate_vncr_va(struct kvm *kvm,
>  		struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
>  		u64 va_start, va_end, va_size;
>  
> +		/* Skip vCPUs whose pseudo-TLB hasn't been allocated yet */
> +		if (!vt)
> +			continue;
> +
>  		if (!vt->valid)
>  			continue;
>  

This looks correct and matches what we already have for
invalidate_vncr_ipa().

But I think this misses the opportunity to squash a whole class of
similar bugs, should we ever have the need for another function that
iterates over all *valid* VNCR pseudo-TLBs.

Since I'm on a train and have nothing better to do, I've written the
following hack.

Thoughts?

	M.

diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index 38f672e940878..f0a9f81a08302 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -897,9 +897,21 @@ static void invalidate_vncr(struct vncr_tlb *vt)
 		clear_fixmap(vncr_fixmap(vt->cpu));
 }
 
+/*
+ * VNCR TLB invalidation occurs from MMU notifiers or TLBI instructions, and
+ * either can race against a vcpu not being onlined yet (no pseudo-TLB
+ * allocated). Similarly, the TLB might be invalid.  Skip those, as they
+ * obviously don't participate in the invalidation at this stage.
+ */
+#define kvm_for_each_vncr_tlb(idx, vcpup, tlbp, kvm)	\
+	kvm_for_each_vcpu(idx, vcpu, kvm)		\
+		if (((tlbp) = vcpu->arch.vncr_tlb) &&	\
+		    (tlbp)->valid)
+
 static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end)
 {
 	struct kvm_vcpu *vcpu;
+	struct vncr_tlb *vt;
 	unsigned long i;
 
 	lockdep_assert_held_write(&kvm->mmu_lock);
@@ -907,24 +919,9 @@ static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end)
 	if (!kvm_has_feat(kvm, ID_AA64MMFR4_EL1, NV_frac, NV2_ONLY))
 		return;
 
-	kvm_for_each_vcpu(i, vcpu, kvm) {
-		struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
+	kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) {
 		u64 ipa_start, ipa_end, ipa_size;
 
-		/*
-		 * Careful here: We end-up here from an MMU notifier,
-		 * and this can race against a vcpu not being onlined
-		 * yet, without the pseudo-TLB being allocated.
-		 *
-		 * Skip those, as they obviously don't participate in
-		 * the invalidation at this stage.
-		 */
-		if (!vt)
-			continue;
-
-		if (!vt->valid)
-			continue;
-
 		ipa_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift,
 							    vt->wr.level));
 		ipa_start = vt->wr.pa & ~(ipa_size - 1);
@@ -954,17 +951,14 @@ static void invalidate_vncr_va(struct kvm *kvm,
 			       struct s1e2_tlbi_scope *scope)
 {
 	struct kvm_vcpu *vcpu;
+	struct vncr_tlb *vt;
 	unsigned long i;
 
 	lockdep_assert_held_write(&kvm->mmu_lock);
 
-	kvm_for_each_vcpu(i, vcpu, kvm) {
-		struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
+	kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) {
 		u64 va_start, va_end, va_size;
 
-		if (!vt->valid)
-			continue;
-
 		va_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift,
 							   vt->wr.level));
 		va_start = vt->gva & ~(va_size - 1);

-- 
Jazz isn't dead. It just smells funny.


^ permalink raw reply related

* Re: [PATCH] KVM: arm64: nv: Skip vCPUs without a pseudo-TLB in invalidate_vncr_va()
From: Hyunwoo Kim @ 2026-06-07 13:36 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
	catalin.marinas, will, linux-arm-kernel, kvmarm, imv4bel
In-Reply-To: <8733yya4ch.wl-maz@kernel.org>

On Sun, Jun 07, 2026 at 02:05:02PM +0100, Marc Zyngier wrote:
> On Sun, 07 Jun 2026 09:43:53 +0100,
> Hyunwoo Kim <imv4bel@gmail.com> wrote:
> > 
> > vncr_tlb is not allocated before a vCPU runs for the first time, so
> > vcpu->arch.vncr_tlb is NULL for a vCPU that has been created but not yet
> > run. Code that iterates over every vCPU's pseudo-TLB must skip those.
> > 
> > invalidate_vncr_va() iterates over the vCPUs with kvm_for_each_vcpu() and
> > dereferences vt->valid without checking whether vncr_tlb is NULL.
> > 
> > While iterating, skip vCPUs whose pseudo-TLB has not been allocated.
> >
> > Fixes: 4ffa72ad8f37 ("KVM: arm64: nv: Add S1 TLB invalidation primitive for VNCR_EL2")
> > Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
> > ---
> >  arch/arm64/kvm/nested.c | 4 ++++
> >  1 file changed, 4 insertions(+)
> > 
> > diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> > index 6f7bc9a9992e..063e079d1d1a 100644
> > --- a/arch/arm64/kvm/nested.c
> > +++ b/arch/arm64/kvm/nested.c
> > @@ -969,6 +969,10 @@ static void invalidate_vncr_va(struct kvm *kvm,
> >  		struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
> >  		u64 va_start, va_end, va_size;
> >  
> > +		/* Skip vCPUs whose pseudo-TLB hasn't been allocated yet */
> > +		if (!vt)
> > +			continue;
> > +
> >  		if (!vt->valid)
> >  			continue;
> >  
> 
> This looks correct and matches what we already have for
> invalidate_vncr_ipa().
> 
> But I think this misses the opportunity to squash a whole class of
> similar bugs, should we ever have the need for another function that
> iterates over all *valid* VNCR pseudo-TLBs.
> 
> Since I'm on a train and have nothing better to do, I've written the
> following hack.
> 
> Thoughts?

Looks like a good direction to me. I confirmed it fixes the issue (as
expected).

How about you submit this patch yourself?

> 
> 	M.
> 
> diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> index 38f672e940878..f0a9f81a08302 100644
> --- a/arch/arm64/kvm/nested.c
> +++ b/arch/arm64/kvm/nested.c
> @@ -897,9 +897,21 @@ static void invalidate_vncr(struct vncr_tlb *vt)
>  		clear_fixmap(vncr_fixmap(vt->cpu));
>  }
>  
> +/*
> + * VNCR TLB invalidation occurs from MMU notifiers or TLBI instructions, and
> + * either can race against a vcpu not being onlined yet (no pseudo-TLB
> + * allocated). Similarly, the TLB might be invalid.  Skip those, as they
> + * obviously don't participate in the invalidation at this stage.
> + */
> +#define kvm_for_each_vncr_tlb(idx, vcpup, tlbp, kvm)	\
> +	kvm_for_each_vcpu(idx, vcpu, kvm)		\

Maybe vcpu -> vcpup?

> +		if (((tlbp) = vcpu->arch.vncr_tlb) &&	\
> +		    (tlbp)->valid)
> +
>  static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end)
>  {
>  	struct kvm_vcpu *vcpu;
> +	struct vncr_tlb *vt;
>  	unsigned long i;
>  
>  	lockdep_assert_held_write(&kvm->mmu_lock);
> @@ -907,24 +919,9 @@ static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end)
>  	if (!kvm_has_feat(kvm, ID_AA64MMFR4_EL1, NV_frac, NV2_ONLY))
>  		return;
>  
> -	kvm_for_each_vcpu(i, vcpu, kvm) {
> -		struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
> +	kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) {
>  		u64 ipa_start, ipa_end, ipa_size;
>  
> -		/*
> -		 * Careful here: We end-up here from an MMU notifier,
> -		 * and this can race against a vcpu not being onlined
> -		 * yet, without the pseudo-TLB being allocated.
> -		 *
> -		 * Skip those, as they obviously don't participate in
> -		 * the invalidation at this stage.
> -		 */
> -		if (!vt)
> -			continue;
> -
> -		if (!vt->valid)
> -			continue;
> -
>  		ipa_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift,
>  							    vt->wr.level));
>  		ipa_start = vt->wr.pa & ~(ipa_size - 1);
> @@ -954,17 +951,14 @@ static void invalidate_vncr_va(struct kvm *kvm,
>  			       struct s1e2_tlbi_scope *scope)
>  {
>  	struct kvm_vcpu *vcpu;
> +	struct vncr_tlb *vt;
>  	unsigned long i;
>  
>  	lockdep_assert_held_write(&kvm->mmu_lock);
>  
> -	kvm_for_each_vcpu(i, vcpu, kvm) {
> -		struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
> +	kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) {
>  		u64 va_start, va_end, va_size;
>  
> -		if (!vt->valid)
> -			continue;
> -
>  		va_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift,
>  							   vt->wr.level));
>  		va_start = vt->gva & ~(va_size - 1);
> 
> -- 
> Jazz isn't dead. It just smells funny.


Best regards,
Hyunwoo Kim


^ permalink raw reply

* Re: [PATCH] drm/rockchip: dsi: Open-code drm_simple_encoder_init()
From: Diogo Silva @ 2026-06-07 11:57 UTC (permalink / raw)
  To: Heiko Stuebner
  Cc: Sandy Huang, Andy Yan, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, dri-devel,
	linux-arm-kernel, linux-rockchip, linux-kernel
In-Reply-To: <5277524.h16uAIiOU7@phil>

Hey Heiko,

> this description is missing a rationale.
>
> Looking at the drm git history, I guess here you could add a second
> paragraph, with something like:
>
> ----------- 8< -----------
> The helpers have been deprecated for years as they only add an an
> intermediate layer between atomic modesetting and the DRM driver.
> ----------- 8< -----------
>
> Shamelessly stolen from the Todo item ;-)

True. I will add the rationale then.

> Any reason for only changing the DSI driver?
>
> Looking at [0] a number of the Rockchip drivers use the same pattern:
> - drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> - drivers/gpu/drm/rockchip/cdn-dp-core.c
> - drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
> - drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
> - drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> - drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
> - drivers/gpu/drm/rockchip/rk3066_hdmi.c
> - drivers/gpu/drm/rockchip/rockchip_lvds.c
> - drivers/gpu/drm/rockchip/rockchip_rgb.c
>
> You can just do all Rockchip ones - even in one patch I think :-) .

Yes, indeed. I wanted to send a tiny patch to check if all my
configurations were ok, but since it looks like they are, I will add the
other rockchip drivers that have the deprecated functions.

Thanks,
Diogo

On Sun, 7 Jun 2026 at 12:59, Heiko Stuebner <heiko@sntech.de> wrote:
>
> Hi Diego,
>
> Am Donnerstag, 4. Juni 2026, 14:32:25 Mitteleuropäische Sommerzeit schrieb Diogo Silva:
> > Remove the dependency on drm_simple_kms_helper by open-coding the
> > drm_simple_encoder_init call.
>
> this description is missing a rationale.
>
> Looking at the drm git history, I guess here you could add a second
> paragraph, with something like:
>
> ----------- 8< -----------
> The helpers have been deprecated for years as they only add an an
> intermediate layer between atomic modesetting and the DRM driver.
> ----------- 8< -----------
>
> Shamelessly stolen from the Todo item ;-)
>
>
> > Signed-off-by: Diogo Silva <diogompaissilva@gmail.com>
> > ---
> >  drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 9 +++++++--
>
> Any reason for only changing the DSI driver?
>
> Looking at [0] a number of the Rockchip drivers use the same pattern:
> - drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> - drivers/gpu/drm/rockchip/cdn-dp-core.c
> - drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
> - drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
> - drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> - drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
> - drivers/gpu/drm/rockchip/rk3066_hdmi.c
> - drivers/gpu/drm/rockchip/rockchip_lvds.c
> - drivers/gpu/drm/rockchip/rockchip_rgb.c
>
> You can just do all Rockchip ones - even in one patch I think :-) .
>
>
> Thanks
> Heiko
>
> [0] https://elixir.bootlin.com/linux/v7.1-rc6/A/ident/drm_simple_encoder_init
>
>
> >  1 file changed, 7 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
> > index 3547d91b25d3..a09b382d208e 100644
> > --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
> > +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
> > @@ -25,7 +25,6 @@
> >  #include <drm/drm_mipi_dsi.h>
> >  #include <drm/drm_of.h>
> >  #include <drm/drm_print.h>
> > -#include <drm/drm_simple_kms_helper.h>
> >
> >  #include "rockchip_drm_drv.h"
> >
> > @@ -825,6 +824,10 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
> >       clk_disable_unprepare(dsi->grf_clk);
> >  }
> >
> > +static const struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
> > +     .destroy = drm_encoder_cleanup,
> > +};
> > +
> >  static const struct drm_encoder_helper_funcs
> >  dw_mipi_dsi_encoder_helper_funcs = {
> >       .atomic_check = dw_mipi_dsi_encoder_atomic_check,
> > @@ -840,7 +843,9 @@ static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
> >       encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
> >                                                            dsi->dev->of_node);
> >
> > -     ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI);
> > +     ret = drm_encoder_init(drm_dev, encoder,
> > +                             &dw_mipi_dsi_encoder_funcs,
> > +                             DRM_MODE_ENCODER_DSI, NULL);
> >       if (ret) {
> >               DRM_ERROR("Failed to initialize encoder with drm\n");
> >               return ret;
> >
>
>
>
>


^ permalink raw reply

* [PATCH 2/3] perf: marvell: Add CN20K LLC-TAD PMU support
From: Geetha sowjanya @ 2026-06-07 12:51 UTC (permalink / raw)
  To: linux-perf-users, linux-kernel, linux-arm-kernel, devicetree
  Cc: mark.rutland, will, krzk+dt, gakula
In-Reply-To: <20260607125101.17778-1-gakula@marvell.com>

CN20K LLC-TAD uses non-standard PFC/PRF page offsets relative to each
TAD region base. Add a TAD_PMU_V3 profile with per-platform register bases,
extend the CN10K event map for new counters, and match the block via OF and
ACPI.

Signed-off-by: Geetha sowjanya <gakula@marvell.com>
---
 drivers/perf/marvell_cn10k_tad_pmu.c | 33 +++++++++++++++++++++++++---
 1 file changed, 30 insertions(+), 3 deletions(-)

diff --git a/drivers/perf/marvell_cn10k_tad_pmu.c b/drivers/perf/marvell_cn10k_tad_pmu.c
index 7a3b659c999a..4daf109b89ee 100644
--- a/drivers/perf/marvell_cn10k_tad_pmu.c
+++ b/drivers/perf/marvell_cn10k_tad_pmu.c
@@ -17,6 +17,8 @@
 
 #define TAD_PRF_OFFSET		0x900
 #define TAD_PFC_OFFSET		0x800
+#define TAD_PRF_NS_OFFSET	0x30900
+#define TAD_PFC_NS_OFFSET	0x30800
 #define TAD_PFC(base, counter)	((base) | ((u64)(counter) << 3))
 #define TAD_PRF(base, counter)	((base) | ((u64)(counter) << 3))
 #define TAD_PRF_CNTSEL_MASK	0xFF
@@ -33,6 +35,7 @@ struct tad_region {
 enum mrvl_tad_pmu_version {
 	TAD_PMU_V1 = 1,
 	TAD_PMU_V2,
+	TAD_PMU_V3,
 };
 
 struct tad_pmu_data {
@@ -80,8 +83,14 @@ static void tad_pmu_start_counter(struct tad_pmu *pmu,
 	if (partid && event_idx > 0x19 && event_idx < 0x21) {
 		partid_filter = TAD_PRF_MATCH_PARTID | TAD_PRF_PARTID_NS |
 				((u64)partid << 11);
+
+		if (pdata->id == TAD_PMU_V3)
+			partid_filter = TAD_PRF_MATCH_PARTID | BIT_ULL(25) |
+				((u64)partid << 10);
 	}
 
+	if (pdata->id == TAD_PMU_V1 && event_idx > 0x24)
+		return;
 
 	for (i = 0; i < pmu->region_cnt; i++) {
 		reg_val = event_idx & 0xFF;
@@ -261,6 +270,18 @@ static struct attribute *tad_pmu_event_attrs[] = {
 	TAD_PMU_EVENT_ATTR(tad_dat_rd_byp, 0x22),
 	TAD_PMU_EVENT_ATTR(tad_ifb_occ, 0x23),
 	TAD_PMU_EVENT_ATTR(tad_req_occ, 0x24),
+	TAD_PMU_EVENT_ATTR(tad_req_msh_out_dtg_evict, 0x25),
+	TAD_PMU_EVENT_ATTR(tad_req_msh_out_ltg_evict, 0x26),
+	TAD_PMU_EVENT_ATTR(tad_rsp_msh_out_mpam, 0x28),
+	TAD_PMU_EVENT_ATTR(tad_replays, 0x29),
+	TAD_PMU_EVENT_ATTR(tad_req_byp0, 0x2a),
+	TAD_PMU_EVENT_ATTR(tad_req_byp1, 0x2b),
+	TAD_PMU_EVENT_ATTR(tad_txreq_byp, 0x2c),
+	TAD_PMU_EVENT_ATTR(tad_time_in_dslp, 0x2d),
+	TAD_PMU_EVENT_ATTR(tad_time_elapsed, 0x2e),
+	TAD_PMU_EVENT_ATTR(tad_req_msh_out_dss_rd_128mrg, 0x2f),
+	TAD_PMU_EVENT_ATTR(tad_req_msh_out_dss_wr_128mrg, 0x30),
+	TAD_PMU_EVENT_ATTR(tad_tot_cycle, 0xff),
 	NULL
 };
 
@@ -426,7 +447,6 @@ static int tad_pmu_probe(struct platform_device *pdev)
 	tad_pmu->region_cnt = tad_cnt;
 
 	tad_pmu->pmu = (struct pmu) {
-
 		.module		= THIS_MODULE,
 		.capabilities	= PERF_PMU_CAP_NO_EXCLUDE |
 				  PERF_PMU_CAP_NO_INTERRUPT,
@@ -440,7 +460,7 @@ static int tad_pmu_probe(struct platform_device *pdev)
 		.read		= tad_pmu_event_counter_read,
 	};
 
-	if (version == TAD_PMU_V1) {
+	if (version == TAD_PMU_V1 || version == TAD_PMU_V3) {
 		tad_pmu->pmu.attr_groups = tad_pmu_attr_groups;
 		tad_pmu->ops		 = &tad_pmu_ops;
 	} else {
@@ -482,6 +502,11 @@ static const struct tad_pmu_data tad_pmu_data = {
 	.tad_pfc_offset = TAD_PFC_OFFSET,
 };
 
+static const struct tad_pmu_data tad_pmu_cn20k_data = {
+	.id   = TAD_PMU_V3,
+	.tad_prf_offset = TAD_PRF_NS_OFFSET,
+	.tad_pfc_offset = TAD_PFC_NS_OFFSET,
+};
 #endif
 
 #ifdef CONFIG_ACPI
@@ -495,6 +520,7 @@ static const struct tad_pmu_data tad_pmu_v2_data = {
 #ifdef CONFIG_OF
 static const struct of_device_id tad_pmu_of_match[] = {
 	{ .compatible = "marvell,cn10k-tad-pmu", .data = &tad_pmu_data },
+	{ .compatible = "marvell,cn20k-tad-pmu", .data = &tad_pmu_cn20k_data },
 	{},
 };
 #endif
@@ -503,6 +529,7 @@ static const struct of_device_id tad_pmu_of_match[] = {
 static const struct acpi_device_id tad_pmu_acpi_match[] = {
 	{"MRVL000B", (kernel_ulong_t)&tad_pmu_data},
 	{"MRVL000D", (kernel_ulong_t)&tad_pmu_v2_data},
+	{"MRVL000F", (kernel_ulong_t)&tad_pmu_cn20k_data},
 	{},
 };
 MODULE_DEVICE_TABLE(acpi, tad_pmu_acpi_match);
@@ -564,6 +591,6 @@ static void __exit tad_pmu_exit(void)
 module_init(tad_pmu_init);
 module_exit(tad_pmu_exit);
 
-MODULE_DESCRIPTION("Marvell CN10K LLC-TAD Perf driver");
+MODULE_DESCRIPTION("Marvell CN10K/CN20K LLC-TAD Perf driver");
 MODULE_AUTHOR("Bhaskara Budiredla <bbudiredla@marvell.com>");
 MODULE_LICENSE("GPL v2");
-- 
2.25.1



^ permalink raw reply related

* [PATCH 1/3] perf: marvell: Add MPAM partid filtering to CN10K TAD PMU
From: Geetha sowjanya @ 2026-06-07 12:50 UTC (permalink / raw)
  To: linux-perf-users, linux-kernel, linux-arm-kernel, devicetree
  Cc: mark.rutland, will, krzk+dt, gakula
In-Reply-To: <20260607125101.17778-1-gakula@marvell.com>

From: Tanmay Jagdale <tanmay@marvell.com>

The TAD PMU exposes counters that can be filtered by MPAM partition id
for a subset of allocation and hit events.

Add a 16-bit partid format attribute (config1) and route counter
programming through variant-specific ops so CN10K keeps MPAM-capable
programming while Odyssey keeps the reduced event set without
advertising partid in sysfs.

Example:
  perf stat -e tad/tad_alloc_any,partid=0x12/ -- <program>

Signed-off-by: Tanmay Jagdale <tanmay@marvell.com>
Signed-off-by: Geetha sowjanya <gakula@marvell.com>
---
 drivers/perf/marvell_cn10k_tad_pmu.c | 151 ++++++++++++++++++++-------
 1 file changed, 112 insertions(+), 39 deletions(-)

diff --git a/drivers/perf/marvell_cn10k_tad_pmu.c b/drivers/perf/marvell_cn10k_tad_pmu.c
index 51ccb0befa05..7a3b659c999a 100644
--- a/drivers/perf/marvell_cn10k_tad_pmu.c
+++ b/drivers/perf/marvell_cn10k_tad_pmu.c
@@ -7,6 +7,7 @@
 #define pr_fmt(fmt) "tad_pmu: " fmt
 
 #include <linux/io.h>
+#include <linux/bits.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/cpuhotplug.h>
@@ -14,11 +15,13 @@
 #include <linux/platform_device.h>
 #include <linux/acpi.h>
 
-#define TAD_PFC_OFFSET		0x800
-#define TAD_PFC(counter)	(TAD_PFC_OFFSET | (counter << 3))
 #define TAD_PRF_OFFSET		0x900
-#define TAD_PRF(counter)	(TAD_PRF_OFFSET | (counter << 3))
+#define TAD_PFC_OFFSET		0x800
+#define TAD_PFC(base, counter)	((base) | ((u64)(counter) << 3))
+#define TAD_PRF(base, counter)	((base) | ((u64)(counter) << 3))
 #define TAD_PRF_CNTSEL_MASK	0xFF
+#define TAD_PRF_MATCH_PARTID	BIT(8)
+#define TAD_PRF_PARTID_NS	BIT(10)
 #define TAD_MAX_COUNTERS	8
 
 #define to_tad_pmu(p) (container_of(p, struct tad_pmu, pmu))
@@ -27,30 +30,92 @@ struct tad_region {
 	void __iomem	*base;
 };
 
+enum mrvl_tad_pmu_version {
+	TAD_PMU_V1 = 1,
+	TAD_PMU_V2,
+};
+
+struct tad_pmu_data {
+	int id;
+	u64 tad_prf_offset;
+	u64 tad_pfc_offset;
+};
+
 struct tad_pmu {
 	struct pmu pmu;
 	struct tad_region *regions;
 	u32 region_cnt;
 	unsigned int cpu;
+	const struct tad_pmu_ops *ops;
+	const struct tad_pmu_data *pdata;
 	struct hlist_node node;
 	struct perf_event *events[TAD_MAX_COUNTERS];
 	DECLARE_BITMAP(counters_map, TAD_MAX_COUNTERS);
 };
 
-enum mrvl_tad_pmu_version {
-	TAD_PMU_V1 = 1,
-	TAD_PMU_V2,
-};
-
-struct tad_pmu_data {
-	int id;
+struct tad_pmu_ops {
+	void (*start_counter)(struct tad_pmu *pmu, struct perf_event *event);
 };
 
 static int tad_pmu_cpuhp_state;
 
+static void tad_pmu_start_counter(struct tad_pmu *pmu,
+				  struct perf_event *event)
+{
+	const struct tad_pmu_data *pdata = pmu->pdata;
+	struct hw_perf_event *hwc = &event->hw;
+	u32 event_idx = event->attr.config;
+	u32 counter_idx = hwc->idx;
+	u64 partid_filter = 0;
+	u64 reg_val;
+	u32 partid;
+	int i;
+
+	partid = (u32)(event->attr.config1 & GENMASK(15, 0));
+
+	for (i = 0; i < pmu->region_cnt; i++)
+		writeq_relaxed(0, pmu->regions[i].base +
+			       TAD_PFC(pdata->tad_pfc_offset, counter_idx));
+
+	if (partid && event_idx > 0x19 && event_idx < 0x21) {
+		partid_filter = TAD_PRF_MATCH_PARTID | TAD_PRF_PARTID_NS |
+				((u64)partid << 11);
+	}
+
+
+	for (i = 0; i < pmu->region_cnt; i++) {
+		reg_val = event_idx & 0xFF;
+		reg_val |= partid_filter;
+		writeq_relaxed(reg_val, pmu->regions[i].base +
+			       TAD_PRF(pdata->tad_prf_offset, counter_idx));
+	}
+}
+
+static void tad_pmu_v2_start_counter(struct tad_pmu *pmu,
+				     struct perf_event *event)
+{
+	const struct tad_pmu_data *pdata = pmu->pdata;
+	struct hw_perf_event *hwc = &event->hw;
+	u32 event_idx = event->attr.config;
+	u32 counter_idx = hwc->idx;
+	u64 reg_val;
+	int i;
+
+	for (i = 0; i < pmu->region_cnt; i++)
+		writeq_relaxed(0, pmu->regions[i].base +
+			       TAD_PFC(pdata->tad_pfc_offset, counter_idx));
+
+	for (i = 0; i < pmu->region_cnt; i++) {
+		reg_val = event_idx & 0xFF;
+		writeq_relaxed(reg_val, pmu->regions[i].base +
+			       TAD_PRF(pdata->tad_prf_offset, counter_idx));
+	}
+}
+
 static void tad_pmu_event_counter_read(struct perf_event *event)
 {
 	struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
+	const struct tad_pmu_data *pdata = tad_pmu->pdata;
 	struct hw_perf_event *hwc = &event->hw;
 	u32 counter_idx = hwc->idx;
 	u64 prev, new;
@@ -60,7 +125,7 @@ static void tad_pmu_event_counter_read(struct perf_event *event)
 		prev = local64_read(&hwc->prev_count);
 		for (i = 0, new = 0; i < tad_pmu->region_cnt; i++)
 			new += readq(tad_pmu->regions[i].base +
-				     TAD_PFC(counter_idx));
+				     TAD_PFC(pdata->tad_pfc_offset, counter_idx));
 	} while (local64_cmpxchg(&hwc->prev_count, prev, new) != prev);
 
 	local64_add(new - prev, &event->count);
@@ -69,16 +134,14 @@ static void tad_pmu_event_counter_read(struct perf_event *event)
 static void tad_pmu_event_counter_stop(struct perf_event *event, int flags)
 {
 	struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
+	const struct tad_pmu_data *pdata = tad_pmu->pdata;
 	struct hw_perf_event *hwc = &event->hw;
 	u32 counter_idx = hwc->idx;
 	int i;
 
-	/* TAD()_PFC() stop counting on the write
-	 * which sets TAD()_PRF()[CNTSEL] == 0
-	 */
 	for (i = 0; i < tad_pmu->region_cnt; i++) {
 		writeq_relaxed(0, tad_pmu->regions[i].base +
-			       TAD_PRF(counter_idx));
+			       TAD_PRF(pdata->tad_prf_offset, counter_idx));
 	}
 
 	tad_pmu_event_counter_read(event);
@@ -89,26 +152,10 @@ static void tad_pmu_event_counter_start(struct perf_event *event, int flags)
 {
 	struct tad_pmu *tad_pmu = to_tad_pmu(event->pmu);
 	struct hw_perf_event *hwc = &event->hw;
-	u32 event_idx = event->attr.config;
-	u32 counter_idx = hwc->idx;
-	u64 reg_val;
-	int i;
 
 	hwc->state = 0;
 
-	/* Typically TAD_PFC() are zeroed to start counting */
-	for (i = 0; i < tad_pmu->region_cnt; i++)
-		writeq_relaxed(0, tad_pmu->regions[i].base +
-			       TAD_PFC(counter_idx));
-
-	/* TAD()_PFC() start counting on the write
-	 * which sets TAD()_PRF()[CNTSEL] != 0
-	 */
-	for (i = 0; i < tad_pmu->region_cnt; i++) {
-		reg_val = event_idx & 0xFF;
-		writeq_relaxed(reg_val,	tad_pmu->regions[i].base +
-			       TAD_PRF(counter_idx));
-	}
+	tad_pmu->ops->start_counter(tad_pmu, event);
 }
 
 static void tad_pmu_event_counter_del(struct perf_event *event, int flags)
@@ -128,7 +175,6 @@ static int tad_pmu_event_counter_add(struct perf_event *event, int flags)
 	struct hw_perf_event *hwc = &event->hw;
 	int idx;
 
-	/* Get a free counter for this event */
 	idx = find_first_zero_bit(tad_pmu->counters_map, TAD_MAX_COUNTERS);
 	if (idx == TAD_MAX_COUNTERS)
 		return -EAGAIN;
@@ -232,7 +278,7 @@ static struct attribute *ody_tad_pmu_event_attrs[] = {
 	TAD_PMU_EVENT_ATTR(tad_hit_ltg, 0x1e),
 	TAD_PMU_EVENT_ATTR(tad_hit_any, 0x1f),
 	TAD_PMU_EVENT_ATTR(tad_tag_rd, 0x20),
-	TAD_PMU_EVENT_ATTR(tad_tot_cycle, 0xFF),
+	TAD_PMU_EVENT_ATTR(tad_tot_cycle, 0xff),
 	NULL
 };
 
@@ -242,9 +288,11 @@ static const struct attribute_group ody_tad_pmu_events_attr_group = {
 };
 
 PMU_FORMAT_ATTR(event, "config:0-7");
+PMU_FORMAT_ATTR(partid, "config1:0-15");
 
 static struct attribute *tad_pmu_format_attrs[] = {
 	&format_attr_event.attr,
+	&format_attr_partid.attr,
 	NULL
 };
 
@@ -253,6 +301,16 @@ static struct attribute_group tad_pmu_format_attr_group = {
 	.attrs = tad_pmu_format_attrs,
 };
 
+static struct attribute *ody_tad_pmu_format_attrs[] = {
+	&format_attr_event.attr,
+	NULL
+};
+
+static struct attribute_group ody_tad_pmu_format_attr_group = {
+	.name = "format",
+	.attrs = ody_tad_pmu_format_attrs,
+};
+
 static ssize_t tad_pmu_cpumask_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
 {
@@ -281,11 +339,19 @@ static const struct attribute_group *tad_pmu_attr_groups[] = {
 
 static const struct attribute_group *ody_tad_pmu_attr_groups[] = {
 	&ody_tad_pmu_events_attr_group,
-	&tad_pmu_format_attr_group,
+	&ody_tad_pmu_format_attr_group,
 	&tad_pmu_cpumask_attr_group,
 	NULL
 };
 
+static const struct tad_pmu_ops tad_pmu_ops = {
+	.start_counter = tad_pmu_start_counter,
+};
+
+static const struct tad_pmu_ops tad_pmu_v2_ops = {
+	.start_counter = tad_pmu_v2_start_counter,
+};
+
 static int tad_pmu_probe(struct platform_device *pdev)
 {
 	const struct tad_pmu_data *dev_data;
@@ -312,6 +378,7 @@ static int tad_pmu_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 	version = dev_data->id;
+	tad_pmu->pdata = dev_data;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res) {
@@ -344,7 +411,6 @@ static int tad_pmu_probe(struct platform_device *pdev)
 	if (!regions)
 		return -ENOMEM;
 
-	/* ioremap the distributed TAD pmu regions */
 	for (i = 0; i < tad_cnt && res->start < res->end; i++) {
 		regions[i].base = devm_ioremap(&pdev->dev,
 					       res->start,
@@ -374,14 +440,16 @@ static int tad_pmu_probe(struct platform_device *pdev)
 		.read		= tad_pmu_event_counter_read,
 	};
 
-	if (version == TAD_PMU_V1)
+	if (version == TAD_PMU_V1) {
 		tad_pmu->pmu.attr_groups = tad_pmu_attr_groups;
-	else
+		tad_pmu->ops		 = &tad_pmu_ops;
+	} else {
 		tad_pmu->pmu.attr_groups = ody_tad_pmu_attr_groups;
+		tad_pmu->ops		 = &tad_pmu_v2_ops;
+	}
 
 	tad_pmu->cpu = raw_smp_processor_id();
 
-	/* Register pmu instance for cpu hotplug */
 	ret = cpuhp_state_add_instance_nocalls(tad_pmu_cpuhp_state,
 					       &tad_pmu->node);
 	if (ret) {
@@ -410,12 +478,17 @@ static void tad_pmu_remove(struct platform_device *pdev)
 #if defined(CONFIG_OF) || defined(CONFIG_ACPI)
 static const struct tad_pmu_data tad_pmu_data = {
 	.id   = TAD_PMU_V1,
+	.tad_prf_offset = TAD_PRF_OFFSET,
+	.tad_pfc_offset = TAD_PFC_OFFSET,
 };
+
 #endif
 
 #ifdef CONFIG_ACPI
 static const struct tad_pmu_data tad_pmu_v2_data = {
 	.id   = TAD_PMU_V2,
+	.tad_prf_offset = TAD_PRF_OFFSET,
+	.tad_pfc_offset = TAD_PFC_OFFSET,
 };
 #endif
 
-- 
2.25.1



^ permalink raw reply related

* [PATCH 0/3] perf: marvell: LLC-TAD PMU MPAM filtering and CN20K support
From: Geetha sowjanya @ 2026-06-07 12:50 UTC (permalink / raw)
  To: linux-perf-users, linux-kernel, linux-arm-kernel, devicetree
  Cc: mark.rutland, will, krzk+dt, gakula

This series extends the Marvell LLC-TAD performance driver used on CN10K
and CN20K systems.

Patch 1 adds optional MPAM partition-id filtering for the subset of TAD
events that support it, exposes a partid field in the PMU format string,
and keeps the reduced Odyssey event surface without advertising partid
where it does not apply.

Patch 2 introduces CN20K LLC-TAD support: non-standard PFC/PRF offsets,
additional programmable events, CN20K-specific MPAM encoding for the V3
profile, and device discovery via DeviceTree and ACPI.


Tanmay Jagdale (1):
  perf: marvell: Add MPAM partid filtering to CN10K TAD PMU

Geetha sowjanya (1):
  perf: marvell: Add CN20K LLC-TAD PMU support
  dt-bindings: perf: marvell: Extend CN10K TAD PMU binding for CN20K

Signed-off-by: Geetha sowjanya <gakula@marvell.com>

-- 
2.25.1


^ permalink raw reply

* [PATCH 3/3] dt-bindings: perf: marvell: Extend CN10K TAD PMU binding for CN20K
From: Geetha sowjanya @ 2026-06-07 12:51 UTC (permalink / raw)
  To: linux-perf-users, linux-kernel, linux-arm-kernel, devicetree
  Cc: mark.rutland, will, krzk+dt, gakula
In-Reply-To: <20260607125101.17778-1-gakula@marvell.com>

Allow marvell,cn20k-tad-pmu alongside marvell,cn10k-tad-pmu, document
CN20K in the title and description, add a maintainer, and include a
CN20K example node with the same required properties as CN10K.

Signed-off-by: Geetha sowjanya <gakula@marvell.com>
---
 .../bindings/perf/marvell-cn10k-tad.yaml      | 20 +++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/Documentation/devicetree/bindings/perf/marvell-cn10k-tad.yaml b/Documentation/devicetree/bindings/perf/marvell-cn10k-tad.yaml
index 362142252667..1612052b59ae 100644
--- a/Documentation/devicetree/bindings/perf/marvell-cn10k-tad.yaml
+++ b/Documentation/devicetree/bindings/perf/marvell-cn10k-tad.yaml
@@ -4,23 +4,27 @@
 $id: http://devicetree.org/schemas/perf/marvell-cn10k-tad.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: Marvell CN10K LLC-TAD performance monitor
+title: Marvell CN10K / CN20K LLC-TAD performance monitor
 
 maintainers:
   - Bhaskara Budiredla <bbudiredla@marvell.com>
+  - Geetha sowjanya <gakula@marvell.com>
 
 description: |
-  The Tag-and-Data units (TADs) maintain coherence and contain CN10K
-  shared on-chip last level cache (LLC). The tad pmu measures the
-  performance of last-level cache. Each tad pmu supports up to eight
-  counters.
+  The Tag-and-Data units (TADs) maintain coherence and contain the
+  shared on-chip last level cache (LLC) on Marvell CN10K and CN20K SoCs.
+  The TAD PMU measures last-level cache performance. Each TAD PMU
+  supports up to eight counters.
 
-  The DT setup comprises of number of tad blocks, the sizes of pmu
-  regions, tad blocks and overall base address of the HW.
+  The DT setup describes the number of TAD blocks, the sizes of PMU
+  regions and TAD pages, and the overall MMIO base of the hardware.
 
 properties:
   compatible:
-    const: marvell,cn10k-tad-pmu
+    items:
+      - enum:
+          - marvell,cn10k-tad-pmu
+          - marvell,cn20k-tad-pmu
 
   reg:
     maxItems: 1
-- 
2.25.1



^ permalink raw reply related

* [PATCH v2] drm/rockchip: dsi: Open-code drm_simple_encoder_init()
From: Diogo Silva @ 2026-06-07 12:37 UTC (permalink / raw)
  To: heiko
  Cc: airlied, andy.yan, diogompaissilva, dri-devel, hjc,
	linux-arm-kernel, linux-kernel, linux-rockchip, maarten.lankhorst,
	mripard, simona, tzimmermann
In-Reply-To: <5277524.h16uAIiOU7@phil>

Simple KMS helper are deprecated since they only add an intermediate
layer between drivers and the atomic modesetting.
This patch removes the dependency on drm_simple_encoder_init from rockchip
DRM drivers by inlining this helper.

Signed-off-by: Diogo Silva <diogompaissilva@gmail.com>
---
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c  | 9 ++++++---
 drivers/gpu/drm/rockchip/cdn-dp-core.c           | 9 ++++++---
 drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c  | 9 +++++++--
 drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c | 8 ++++++--
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c      | 8 ++++++--
 drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c   | 8 ++++++--
 drivers/gpu/drm/rockchip/rk3066_hdmi.c           | 8 ++++++--
 drivers/gpu/drm/rockchip/rockchip_lvds.c         | 9 +++++++--
 drivers/gpu/drm/rockchip/rockchip_rgb.c          | 8 ++++++--
 9 files changed, 56 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 96bd3dd239d2..ca6701319f2f 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -30,7 +30,6 @@
 #include <drm/drm_panel.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
 
 #include "rockchip_drm_drv.h"
 
@@ -317,6 +316,10 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
 	return 0;
 }
 
+static const struct drm_encoder_funcs rockchip_dp_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
 static const struct drm_encoder_helper_funcs rockchip_dp_encoder_helper_funcs = {
 	.mode_fixup = rockchip_dp_drm_encoder_mode_fixup,
 	.mode_set = rockchip_dp_drm_encoder_mode_set,
@@ -369,8 +372,8 @@ static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
 							     dev->of_node);
 	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 
-	ret = drm_simple_encoder_init(drm_dev, encoder,
-				      DRM_MODE_ENCODER_TMDS);
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_dp_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS, NULL);
 	if (ret) {
 		DRM_ERROR("failed to initialize encoder with drm\n");
 		return ret;
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index 177e30445ee8..060ffca1f3d8 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -23,7 +23,6 @@
 #include <drm/drm_of.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
 
 #include "cdn-dp-core.h"
 #include "cdn-dp-reg.h"
@@ -671,6 +670,10 @@ static int cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
 	return 0;
 }
 
+static const struct drm_encoder_funcs cdn_dp_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
 static const struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
 	.atomic_check = cdn_dp_encoder_atomic_check,
 };
@@ -988,8 +991,8 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 							     dev->of_node);
 	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 
-	ret = drm_simple_encoder_init(drm_dev, encoder,
-				      DRM_MODE_ENCODER_TMDS);
+	ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS, NULL);
 	if (ret) {
 		DRM_ERROR("failed to initialize encoder with drm\n");
 		return ret;
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index 3547d91b25d3..a09b382d208e 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -25,7 +25,6 @@
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_of.h>
 #include <drm/drm_print.h>
-#include <drm/drm_simple_kms_helper.h>
 
 #include "rockchip_drm_drv.h"
 
@@ -825,6 +824,10 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
 	clk_disable_unprepare(dsi->grf_clk);
 }
 
+static const struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
 static const struct drm_encoder_helper_funcs
 dw_mipi_dsi_encoder_helper_funcs = {
 	.atomic_check = dw_mipi_dsi_encoder_atomic_check,
@@ -840,7 +843,9 @@ static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dsi->dev->of_node);
 
-	ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI);
+	ret = drm_encoder_init(drm_dev, encoder,
+				&dw_mipi_dsi_encoder_funcs,
+				DRM_MODE_ENCODER_DSI, NULL);
 	if (ret) {
 		DRM_ERROR("Failed to initialize encoder with drm\n");
 		return ret;
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
index 0aea764e29b2..687afc5590cd 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
@@ -23,7 +23,6 @@
 #include <drm/bridge/dw_mipi_dsi2.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_of.h>
-#include <drm/drm_simple_kms_helper.h>
 
 #include <uapi/linux/videodev2.h>
 
@@ -275,6 +274,10 @@ dw_mipi_dsi2_encoder_atomic_check(struct drm_encoder *encoder,
 	return 0;
 }
 
+static const struct drm_encoder_funcs dw_mipi_dsi2_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
 static const struct drm_encoder_helper_funcs
 dw_mipi_dsi2_encoder_helper_funcs = {
 	.atomic_enable = dw_mipi_dsi2_encoder_atomic_enable,
@@ -290,7 +293,8 @@ static int rockchip_dsi2_drm_create_encoder(struct dw_mipi_dsi2_rockchip *dsi2,
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dsi2->dev->of_node);
 
-	ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI);
+	ret = drm_encoder_init(drm_dev, encoder, &dw_mipi_dsi2_encoder_funcs,
+			       DRM_MODE_ENCODER_DSI, NULL);
 	if (ret) {
 		dev_err(dsi2->dev, "Failed to initialize encoder with drm\n");
 		return ret;
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 0dc1eb5d2ae3..906264d65db3 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -16,7 +16,6 @@
 #include <drm/drm_edid.h>
 #include <drm/drm_of.h>
 #include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
 
 #include "rockchip_drm_drv.h"
 
@@ -322,6 +321,10 @@ dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
 	return 0;
 }
 
+static const struct drm_encoder_funcs dw_hdmi_rockchip_encoder_funcs = {
+	.destroy    = drm_encoder_cleanup,
+};
+
 static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
 	.mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup,
 	.mode_set   = dw_hdmi_rockchip_encoder_mode_set,
@@ -604,7 +607,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	}
 
 	drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
-	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+	drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS, NULL);
 
 	platform_set_drvdata(pdev, hdmi);
 
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
index 1a09bcc96c3e..ed665352618f 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -23,7 +23,6 @@
 #include <drm/drm_bridge_connector.h>
 #include <drm/drm_of.h>
 #include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
 
 #include "rockchip_drm_drv.h"
 
@@ -160,6 +159,10 @@ dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
 	return ret;
 }
 
+static const struct drm_encoder_funcs dw_hdmi_qp_rockchip_encoder_funcs = {
+	.destroy	= drm_encoder_cleanup,
+};
+
 static const struct
 drm_encoder_helper_funcs dw_hdmi_qp_rockchip_encoder_helper_funcs = {
 	.enable		= dw_hdmi_qp_rockchip_encoder_enable,
@@ -586,7 +589,8 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
 		return ret;
 
 	drm_encoder_helper_add(encoder, &dw_hdmi_qp_rockchip_encoder_helper_funcs);
-	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+	drm_encoder_init(drm, encoder, &dw_hdmi_qp_rockchip_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS, NULL);
 
 	platform_set_drvdata(pdev, hdmi);
 
diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
index 9066ee2d1dff..a21df6f380f5 100644
--- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c
+++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
@@ -12,7 +12,6 @@
 #include <drm/drm_of.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
 
 #include <linux/clk.h>
 #include <linux/mfd/syscon.h>
@@ -454,6 +453,10 @@ rk3066_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
 	return 0;
 }
 
+static const struct drm_encoder_funcs rk3066_hdmi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
 static const
 struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = {
 	.atomic_check   = rk3066_hdmi_encoder_atomic_check,
@@ -696,7 +699,8 @@ rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi)
 		return -EPROBE_DEFER;
 
 	drm_encoder_helper_add(encoder, &rk3066_hdmi_encoder_helper_funcs);
-	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+	drm_encoder_init(drm, encoder, &rk3066_hdmi_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS, NULL);
 
 	hdmi->bridge.driver_private = hdmi;
 	hdmi->bridge.funcs = &rk3066_hdmi_bridge_funcs;
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index 75f898a10cbc..c3b30c622873 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -24,7 +24,6 @@
 #include <drm/drm_panel.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_lvds.h"
@@ -427,6 +426,10 @@ static void px30_lvds_encoder_disable(struct drm_encoder *encoder)
 	drm_panel_unprepare(lvds->panel);
 }
 
+static const struct drm_encoder_funcs rockchip_lvds_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
 static const
 struct drm_encoder_helper_funcs rk3288_lvds_encoder_helper_funcs = {
 	.enable = rk3288_lvds_encoder_enable,
@@ -594,7 +597,9 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dev->of_node);
 
-	ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_LVDS);
+	ret = drm_encoder_init(drm_dev, encoder,
+				      &rockchip_lvds_encoder_funcs,
+				      DRM_MODE_ENCODER_LVDS, NULL);
 	if (ret < 0) {
 		drm_err(drm_dev,
 			"failed to initialize encoder: %d\n", ret);
diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c
index 5c0c6e2cc28d..b5b8322a1260 100644
--- a/drivers/gpu/drm/rockchip/rockchip_rgb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c
@@ -17,7 +17,6 @@
 #include <drm/drm_panel.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_rgb.h"
@@ -65,6 +64,10 @@ rockchip_rgb_encoder_atomic_check(struct drm_encoder *encoder,
 	return 0;
 }
 
+static const struct drm_encoder_funcs rockchip_rgb_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
 static const
 struct drm_encoder_helper_funcs rockchip_rgb_encoder_helper_funcs = {
 	.atomic_check = rockchip_rgb_encoder_atomic_check,
@@ -127,7 +130,8 @@ struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
 	encoder = &rgb->encoder.encoder;
 	encoder->possible_crtcs = drm_crtc_mask(crtc);
 
-	ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_NONE);
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_rgb_encoder_funcs,
+			       DRM_MODE_ENCODER_NONE, NULL);
 	if (ret < 0) {
 		DRM_DEV_ERROR(drm_dev->dev,
 			      "failed to initialize encoder: %d\n", ret);
-- 
2.51.2



^ permalink raw reply related

* Re: [PATCH v7 0/5] Add microchip sama7d65 SoC I3C support
From: Claudiu Beznea @ 2026-06-07 12:19 UTC (permalink / raw)
  To: Manikandan Muralidharan, alexandre.belloni, Frank.Li, robh,
	krzk+dt, conor+dt, nicolas.ferre, linux, mturquette, sboyd,
	bmasney, aubin.constans, Ryan.Wanner, romain.sioen, tytso,
	cristian.birsan, adrian.hunter, npitre, linux-i3c, devicetree,
	linux-kernel, linux-arm-kernel, linux-clk
In-Reply-To: <20260525092405.1514213-1-manikandan.m@microchip.com>



On 5/25/26 12:24, Manikandan Muralidharan wrote:
> Durai Manickam KR (2):
>    ARM: dts: microchip: add I3C controller

Applied to at91-dt, thanks!

> 
> Manikandan Muralidharan (3):
>    ARM: configs: at91: sama7: add sama7d65 i3c-hci

Applied to at91-defconfig, thanks!


^ permalink raw reply

* Re: [PATCH] drm/rockchip: dsi: Open-code drm_simple_encoder_init()
From: Heiko Stuebner @ 2026-06-07 11:00 UTC (permalink / raw)
  To: Sandy Huang, Andy Yan, Diogo Silva
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, dri-devel, linux-arm-kernel, linux-rockchip,
	linux-kernel, Diogo Silva
In-Reply-To: <5277524.h16uAIiOU7@phil>

Am Sonntag, 7. Juni 2026, 12:59:00 Mitteleuropäische Sommerzeit schrieb Heiko Stuebner:
> Hi Diego,

sorry for mangling your name, Diogo.


Heiko




^ permalink raw reply

* Re: [PATCH] drm/rockchip: dsi: Open-code drm_simple_encoder_init()
From: Heiko Stuebner @ 2026-06-07 10:59 UTC (permalink / raw)
  To: Sandy Huang, Andy Yan, Diogo Silva
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, dri-devel, linux-arm-kernel, linux-rockchip,
	linux-kernel, Diogo Silva
In-Reply-To: <20260604123224.192543-2-diogompaissilva@gmail.com>

Hi Diego,

Am Donnerstag, 4. Juni 2026, 14:32:25 Mitteleuropäische Sommerzeit schrieb Diogo Silva:
> Remove the dependency on drm_simple_kms_helper by open-coding the
> drm_simple_encoder_init call.

this description is missing a rationale.

Looking at the drm git history, I guess here you could add a second
paragraph, with something like:

----------- 8< -----------
The helpers have been deprecated for years as they only add an an
intermediate layer between atomic modesetting and the DRM driver.
----------- 8< -----------

Shamelessly stolen from the Todo item ;-)


> Signed-off-by: Diogo Silva <diogompaissilva@gmail.com>
> ---
>  drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 9 +++++++--

Any reason for only changing the DSI driver?

Looking at [0] a number of the Rockchip drivers use the same pattern:
- drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
- drivers/gpu/drm/rockchip/cdn-dp-core.c
- drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
- drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
- drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
- drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
- drivers/gpu/drm/rockchip/rk3066_hdmi.c
- drivers/gpu/drm/rockchip/rockchip_lvds.c
- drivers/gpu/drm/rockchip/rockchip_rgb.c

You can just do all Rockchip ones - even in one patch I think :-) .


Thanks
Heiko

[0] https://elixir.bootlin.com/linux/v7.1-rc6/A/ident/drm_simple_encoder_init


>  1 file changed, 7 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
> index 3547d91b25d3..a09b382d208e 100644
> --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
> @@ -25,7 +25,6 @@
>  #include <drm/drm_mipi_dsi.h>
>  #include <drm/drm_of.h>
>  #include <drm/drm_print.h>
> -#include <drm/drm_simple_kms_helper.h>
>  
>  #include "rockchip_drm_drv.h"
>  
> @@ -825,6 +824,10 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
>  	clk_disable_unprepare(dsi->grf_clk);
>  }
>  
> +static const struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
> +	.destroy = drm_encoder_cleanup,
> +};
> +
>  static const struct drm_encoder_helper_funcs
>  dw_mipi_dsi_encoder_helper_funcs = {
>  	.atomic_check = dw_mipi_dsi_encoder_atomic_check,
> @@ -840,7 +843,9 @@ static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
>  	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
>  							     dsi->dev->of_node);
>  
> -	ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI);
> +	ret = drm_encoder_init(drm_dev, encoder,
> +				&dw_mipi_dsi_encoder_funcs,
> +				DRM_MODE_ENCODER_DSI, NULL);
>  	if (ret) {
>  		DRM_ERROR("Failed to initialize encoder with drm\n");
>  		return ret;
> 






^ permalink raw reply

* Re: [PATCH v2] nvme-apple: Prevent shared tags across queues on Apple A11
From: Sven Peter @ 2026-06-07  9:40 UTC (permalink / raw)
  To: Nick Chan, Janne Grunau, Neal Gompa, Keith Busch, Jens Axboe,
	Christoph Hellwig, Sagi Grimberg
  Cc: asahi, linux-arm-kernel, linux-nvme, linux-kernel, stable
In-Reply-To: <20260607-prevent-tag-collision-t8015-v2-1-dc4ef4fb42bc@gmail.com>

On 07.06.26 08:10, Nick Chan wrote:
> On Apple A11, tags of pending commands must be unique across the admin
> and IO queues, else the firmware crashes with
> "duplicate tag error for tag N", with N being the tag.
> 
> Apply the existing workaround for M1 of reserving two tags for the admin
> queue to A11.
> 
> Cc: stable@vger.kernel.org
> Fixes: 04d8ecf37b5e ("nvme: apple: Add Apple A11 support")
> Signed-off-by: Nick Chan <towinchenmi@gmail.com>
> ---

Reviewed-by: Sven Peter <sven@kernel.org>


Best,

Sven



^ permalink raw reply

* Re: [PATCH v6 1/3] dt-bindings: vendor-prefixes: add vicharak
From: Krzysztof Kozlowski @ 2026-06-07  9:13 UTC (permalink / raw)
  To: Hrushiraj Gandhi, Heiko Stuebner
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, devicetree,
	linux-arm-kernel, linux-rockchip, linux-kernel
In-Reply-To: <20260601162143.170030-2-hrushirajg23@gmail.com>

On 01/06/2026 18:21, Hrushiraj Gandhi wrote:
> Add the vendor prefix for Vicharak Computers Pvt. Ltd.
> 
> Signed-off-by: Hrushiraj Gandhi <hrushirajg23@gmail.com>
> ---
>  Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
>  1 file changed, 2 insertions(+)
> 

Drop the patch, it was already sent by someone else.

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v2 1/2] arm64: dts: rockchip: add Vicharak Axon board
From: Krzysztof Kozlowski @ 2026-06-07  8:45 UTC (permalink / raw)
  To: Hrushiraj Gandhi
  Cc: Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	devicetree, linux-arm-kernel, linux-rockchip, linux-kernel
In-Reply-To: <20260601132529.139172-2-hrushirajg23@gmail.com>

On Mon, Jun 01, 2026 at 06:55:28PM +0530, Hrushiraj Gandhi wrote:
> Add initial support for the Vicharak Axon single-board computer based on
> the Rockchip RK3588 SoC.
> 
> The board supports:
> 
> - eMMC storage
> - microSD card
> - Gigabit Ethernet
> - HDMI output (dual HDMI)
> - HDMI input
> - USB 2.0 host ports
> - PCIe 2.0 slots
> - PCIe 3.0 x4 slot
> - SATA
> - RTC
> - Status LEDs
> 
> The board uses an RK806 PMIC and provides the regulators required by
> the RK3588 SoC.
> 
> Signed-off-by: Hrushiraj Gandhi <hrushirajg23@gmail.com>
> ---
>  arch/arm64/boot/dts/rockchip/Makefile         |    1 +
>  .../dts/rockchip/rk3588-vicharak-axon.dts     | 1038 +++++++++++++++++
>  2 files changed, 1039 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/rockchip/rk3588-vicharak-axon.dts
> 
> diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
> index cb55c6b70d0e..fcb115b02eff 100644
> --- a/arch/arm64/boot/dts/rockchip/Makefile
> +++ b/arch/arm64/boot/dts/rockchip/Makefile
> @@ -179,6 +179,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-rock-4d.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3582-radxa-e52c.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-armsom-sige7.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-armsom-w3.dtb
> +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-vicharak-axon.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-coolpi-cm5-evb.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-coolpi-cm5-genbook.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-edgeble-neu6a-io.dtb
> diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vicharak-axon.dts b/arch/arm64/boot/dts/rockchip/rk3588-vicharak-axon.dts
> new file mode 100644
> index 000000000000..fecd13a52e88
> --- /dev/null
> +++ b/arch/arm64/boot/dts/rockchip/rk3588-vicharak-axon.dts
> @@ -0,0 +1,1038 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +
> +/dts-v1/;
> +
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/input/linux-event-codes.h>
> +#include <dt-bindings/leds/common.h>
> +#include <dt-bindings/pinctrl/rockchip.h>
> +#include <dt-bindings/soc/rockchip,vop2.h>
> +#include <dt-bindings/usb/pd.h>
> +#include "rk3588.dtsi"
> +
> +/ {
> +	model = "Vicharak Axon";
> +	compatible = "vicharak,axon", "rockchip,rk3588";
> +
> +	aliases {
> +		mmc0 = &sdmmc;
> +		mmc1 = &sdhci;
> +		mmc2 = &sdio;
> +		serial2 = &uart2;
> +	};
> +
> +	chosen {
> +		stdout-path = "serial2:1500000n8";
> +	};
> +
> +	hdmi0-con {
> +		compatible = "hdmi-connector";
> +		type = "a";
> +
> +		port {
> +			hdmi0_con_in: endpoint {
> +				remote-endpoint = <&hdmi0_out_con>;
> +			};
> +		};
> +	};
> +
> +	hdmi1-con {
> +		compatible = "hdmi-connector";
> +		type = "a";
> +
> +		port {
> +			hdmi1_con_in: endpoint {
> +				remote-endpoint = <&hdmi1_out_con>;
> +			};
> +		};
> +	};
> +
> +	leds {
> +		compatible = "gpio-leds";
> +		status = "okay";

Which DTSI disabled it?

> +
> +		power_led: power-led {
> +			color = <LED_COLOR_ID_GREEN>;
> +			function = LED_FUNCTION_STATUS;
> +			gpios = <&pca9554 0 GPIO_ACTIVE_HIGH>;
> +			linux,default-trigger = "heartbeat";
> +		};
> +
> +		status_led: status-led {
> +			color = <LED_COLOR_ID_RED>;
> +			function = LED_FUNCTION_STATUS;
> +			gpios = <&pca9554 1 GPIO_ACTIVE_HIGH>;
> +			linux,default-trigger = "none";
> +		};
> +	};
> +
> +	pcie20_avdd0v85: regulator-pcie20-avdd0v85 {

Unused, drop. Don't add useless/non-controllable supplies without
consumers. They bring no benefits, except more review, slower kernel,
more code to maintain.

> +		compatible = "regulator-fixed";
> +		regulator-name = "pcie20_avdd0v85";
> +		regulator-boot-on;
> +		regulator-always-on;
> +		regulator-min-microvolt = <850000>;
> +		regulator-max-microvolt = <850000>;
> +		vin-supply = <&vdd_0v85_s0>;
> +	};
> +
> +	pcie20_avdd1v8: regulator-pcie20-avdd1v8 {

No users

> +		compatible = "regulator-fixed";
> +		regulator-name = "pcie20_avdd1v8";
> +		regulator-boot-on;
> +		regulator-always-on;
> +		regulator-min-microvolt = <1800000>;
> +		regulator-max-microvolt = <1800000>;
> +		vin-supply = <&avcc_1v8_s0>;
> +	};
> +
> +	pcie30_avdd0v75: regulator-pcie30-avdd0v75 {

Is any of this real?

> +		compatible = "regulator-fixed";
> +		regulator-name = "pcie30_avdd0v75";
> +		regulator-boot-on;
> +		regulator-always-on;
> +		regulator-min-microvolt = <750000>;
> +		regulator-max-microvolt = <750000>;
> +		vin-supply = <&vdd_0v75_s0>;
> +	};
> +
> +	pcie30_avdd1v8: regulator-pcie30-avdd1v8 {
> +		compatible = "regulator-fixed";
> +		regulator-name = "pcie30_avdd1v8";
> +		regulator-boot-on;
> +		regulator-always-on;
> +		regulator-min-microvolt = <1800000>;
> +		regulator-max-microvolt = <1800000>;
> +		vin-supply = <&avcc_1v8_s0>;
> +	};
> +
> +	sata_vcc_5v0: regulator-sata-vcc-5v0 {
> +		compatible = "regulator-fixed";
> +		regulator-name = "sata_vcc_5v0";
> +		startup-delay-us = <5000>;
> +		regulator-boot-on;
> +		regulator-always-on;
> +		regulator-min-microvolt = <5000000>;
> +		regulator-max-microvolt = <5000000>;
> +		vin-supply = <&vcc12v_dcin>;
> +	};
> +
> +	sdio_pwrseq: sdio-pwrseq {
> +		compatible = "mmc-pwrseq-simple";
> +		clocks = <&hym8563>;
> +		clock-names = "ext_clock";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&sdio_reset_wifi>;
> +		reset-gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>;
> +		status = "okay";
> +	};
> +
> +	vcc0_4v0: vcc4v0_sys: regulator-vcc0-4v0 {

Odd sorting. And still not used!

> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc0_4v0";
> +		regulator-always-on;
> +		regulator-boot-on;
> +		regulator-min-microvolt = <4000000>;
> +		regulator-max-microvolt = <4000000>;
> +		vin-supply = <&vcc12v_dcin>;
> +	};
> +
> +	vcc12v_dcin: regulator-vcc12v-dcin {

Also not used

> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc12v_dcin";
> +		regulator-always-on;
> +		regulator-boot-on;
> +		regulator-min-microvolt = <12000000>;
> +		regulator-max-microvolt = <12000000>;
> +	};
> +
> +	vcc3v3_io_expander: regulator-vcc3v3-io-expander {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc3v3_io_expander";
> +		regulator-boot-on;
> +		regulator-always-on;
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +	};
> +
> +	vcc3v3_pcie20_sata30: regulator-vcc3v3-pcie20-sata30 {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc3v3_pcie20_sata30";
> +		regulator-boot-on;
> +		regulator-always-on;
> +		enable-active-high;
> +		gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-min-microvolt = <3300000>;
> +		vin-supply = <&vcc12v_dcin>;
> +	};

...

> +
> +&i2c0 {
> +	pinctrl-0 = <&i2c0m2_xfer>;
> +	pinctrl-names = "default";
> +	status = "okay";
> +
> +	vdd_cpu_big0_s0: regulator@42 {
> +		compatible = "rockchip,rk8602";
> +		fcs,suspend-voltage-selector = <1>;
> +		reg = <0x42>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		regulator-max-microvolt = <1050000>;
> +		regulator-min-microvolt = <550000>;
> +		regulator-name = "vdd_cpu_big0_s0";
> +		regulator-ramp-delay = <2300>;
> +		vin-supply = <&vcc5v0_sys>;
> +
> +		regulator-state-mem {
> +			regulator-off-in-suspend;
> +		};
> +	};
> +
> +	vdd_cpu_big1_s0: regulator@43 {
> +		compatible = "rockchip,rk8603", "rockchip,rk8602";
> +		fcs,suspend-voltage-selector = <1>;
> +		reg = <0x43>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		regulator-max-microvolt = <1050000>;
> +		regulator-min-microvolt = <550000>;
> +		regulator-name = "vdd_cpu_big1_s0";
> +		regulator-ramp-delay = <2300>;
> +		vin-supply = <&vcc5v0_sys>;
> +
> +		regulator-state-mem {
> +			regulator-off-in-suspend;
> +		};
> +	};
> +
> +	hym8563: rtc@51 {
> +		compatible = "haoyu,hym8563";
> +		reg = <0x51>;
> +		#clock-cells = <0>;
> +		clock-output-names = "hym8563";
> +		interrupt-parent = <&gpio0>;
> +		interrupts = <RK_PB0 IRQ_TYPE_LEVEL_LOW>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&rtc_int>;
> +		wakeup-source;
> +		status = "okay";


Why is this enabled but regulator above is not? What logic drivers this?


> +	};
> +};
> +
> +&i2c1 {
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&i2c1m2_xfer>;
> +	status = "okay";
> +
> +	vdd_npu_s0: regulator@42 {
> +		compatible = "rockchip,rk8602";
> +		reg = <0x42>;
> +		fcs,suspend-voltage-selector = <1>;
> +		regulator-name = "vdd_npu_s0";
> +		regulator-boot-on;
> +		regulator-enable-ramp-delay = <500>;
> +		regulator-min-microvolt = <550000>;
> +		regulator-max-microvolt = <950000>;
> +		regulator-ramp-delay = <2300>;
> +		vin-supply = <&vcc5v0_sys>;
> +
> +		regulator-state-mem {
> +			regulator-off-in-suspend;
> +		};
> +	};
> +};
> +
> +&i2c6 {
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&i2c6m0_xfer>;
> +	status = "okay";
> +
> +	pca9554: gpio@24 {
> +		compatible = "nxp,pca9554";
> +		#gpio-cells = <2>;
> +		gpio-controller;
> +		reg = <0x24>;
> +		vcc-supply = <&vcc3v3_io_expander>;
> +		status = "okay";

Who disabled it?

Best regards,
Krzysztof



^ permalink raw reply

* [PATCH] KVM: arm64: nv: Skip vCPUs without a pseudo-TLB in invalidate_vncr_va()
From: Hyunwoo Kim @ 2026-06-07  8:43 UTC (permalink / raw)
  To: maz, oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
	catalin.marinas, will
  Cc: linux-arm-kernel, kvmarm, imv4bel

vncr_tlb is not allocated before a vCPU runs for the first time, so
vcpu->arch.vncr_tlb is NULL for a vCPU that has been created but not yet
run. Code that iterates over every vCPU's pseudo-TLB must skip those.

invalidate_vncr_va() iterates over the vCPUs with kvm_for_each_vcpu() and
dereferences vt->valid without checking whether vncr_tlb is NULL.

While iterating, skip vCPUs whose pseudo-TLB has not been allocated.

Fixes: 4ffa72ad8f37 ("KVM: arm64: nv: Add S1 TLB invalidation primitive for VNCR_EL2")
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
---
 arch/arm64/kvm/nested.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index 6f7bc9a9992e..063e079d1d1a 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -969,6 +969,10 @@ static void invalidate_vncr_va(struct kvm *kvm,
 		struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
 		u64 va_start, va_end, va_size;
 
+		/* Skip vCPUs whose pseudo-TLB hasn't been allocated yet */
+		if (!vt)
+			continue;
+
 		if (!vt->valid)
 			continue;
 
-- 
2.43.0



^ permalink raw reply related

* Re: [PATCH v2 2/2] dt-bindings: arm: rockchip: add Vicharak Axon board
From: Krzysztof Kozlowski @ 2026-06-07  8:42 UTC (permalink / raw)
  To: Hrushiraj Gandhi
  Cc: Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	devicetree, linux-arm-kernel, linux-rockchip, linux-kernel
In-Reply-To: <20260601132529.139172-3-hrushirajg23@gmail.com>

On Mon, Jun 01, 2026 at 06:55:29PM +0530, Hrushiraj Gandhi wrote:
> Add the device tree binding for the Vicharak Axon single-board
> computer based on the Rockchip RK3588 SoC, and add the vicharak
> vendor prefix to the vendor-prefixes list.
> 
> Signed-off-by: Hrushiraj Gandhi <hrushirajg23@gmail.com>
> ---
>  Documentation/devicetree/bindings/arm/rockchip.yaml    | 5 +++++
>  Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
>  2 files changed, 7 insertions(+)

Please organize the patch documenting the compatible (DT bindings)
before the patch using that compatible.
See also: https://elixir.bootlin.com/linux/v6.14-rc6/source/Documentation/devicetree/bindings/submitting-patches.rst#L46

Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Best regards,
Krzysztof



^ permalink raw reply

* Re: [PATCH bpf-next] arm64: mm: Complete the PTE store in ptep_try_set()
From: bot+bpf-ci @ 2026-06-07  8:38 UTC (permalink / raw)
  To: tj, catalin.marinas, will, ast
  Cc: david, arighi, memxor, akpm, rppt, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, emil, void, changwoo, linux-arm-kernel,
	linux-mm, bpf, linux-kernel, ast, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, clm, ihor.solodrai
In-Reply-To: <088f52fd25860ca961449d53f91b214a@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 1949 bytes --]

> diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
> index 3ce0f2a6cab6..dc8525431273 100644
> --- a/arch/arm64/include/asm/pgtable.h
> +++ b/arch/arm64/include/asm/pgtable.h
> @@ -1838,7 +1838,11 @@ static inline bool ptep_try_set(pte_t *ptep, pte_t new_pte)
>  {
>  	pteval_t old = 0;
>
> -	return try_cmpxchg(&pte_val(*ptep), &old, pte_val(new_pte));
> +	if (!try_cmpxchg(&pte_val(*ptep), &old, pte_val(new_pte)))
> +		return false;
> +
> +	__set_pte_complete(new_pte);

An earlier posting of this drew a question on the mailing list about
__set_pte_complete() deferring the barriers in some contexts:

  https://lore.kernel.org/all/20260607081229.12BC21F00893@smtp.kernel.org/

The concern is that __set_pte_complete() delegates to
queue_pte_barriers(), which only sets TIF_LAZY_MMU_PENDING and defers the
dsb when is_lazy_mmu_mode_active() is true.

The scenario raised is a synchronous BPF arena fault (for example via a
kprobe, where in_interrupt() is false) interrupting a task that is inside a
lazy MMU block such as zap_pte_range(). There is_lazy_mmu_mode_active()
would be true, so the dsb is deferred, and the fault handler could return
before the memory system observes the new PTE. The faulting instruction
would then retry and fault again.

Can this path actually loop, or is the deferred barrier guaranteed to be
flushed before the faulting instruction is retried?

Note this deferral mechanism is the same one used by the existing
__set_pte() path that this change deliberately mirrors, and the comment
above came from an automated review rather than a human, so the scenario
has not been confirmed.

> +	return true;
>  }
>  #define ptep_try_set ptep_try_set

---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/27087148382

^ permalink raw reply

* Re: [PATCH v4 1/3] dt-bindings: arm: sunxi: Add NetCube Systems OpenNMC (dobermann)
From: Krzysztof Kozlowski @ 2026-06-07  8:25 UTC (permalink / raw)
  To: Lukas Schmid
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, Maxime Ripard, devicetree,
	linux-arm-kernel, linux-sunxi, linux-kernel, linux-riscv
In-Reply-To: <20260606205452.2386930-2-lukas.schmid@netcube.li>

On Sat, Jun 06, 2026 at 10:54:41PM +0200, Lukas Schmid wrote:
> The OpenNMC is an open replacement for APC SmartSlot management cards
> based on the Nagami System-on-Module.
> 
> Signed-off-by: Lukas Schmid <lukas.schmid@netcube.li>
> ---
>  Documentation/devicetree/bindings/arm/sunxi.yaml | 1 +
>  1 file changed, 1 insertion(+)

Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Best regards,
Krzysztof



^ permalink raw reply

* [PATCH bpf-next] arm64: mm: Complete the PTE store in ptep_try_set()
From: Tejun Heo @ 2026-06-07  7:59 UTC (permalink / raw)
  To: Catalin Marinas, Will Deacon, Alexei Starovoitov
  Cc: David Hildenbrand, Andrea Righi, Kumar Kartikeya Dwivedi,
	Andrew Morton, Mike Rapoport, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Yonghong Song,
	Emil Tsalapatis, David Vernet, Changwoo Min, linux-arm-kernel,
	linux-mm, bpf, linux-kernel
In-Reply-To: <aiRFcz78QTZdIHHB@arm.com>

ptep_try_set() installs the new entry with try_cmpxchg() but, unlike
__set_pte(), never calls __set_pte_complete(). On arm64, installing a valid
kernel PTE requires barriers afterward so a subsequent access observes it.
Without them the access can fault instead of reaching the freshly installed
page.

Call __set_pte_complete() after a successful cmpxchg, mirroring
__set_pte().

Fixes: 258df8fce42f ("mm: Add ptep_try_set() for lockless empty-slot installs")
Suggested-by: Catalin Marinas <catalin.marinas@arm.com>
Link: https://lore.kernel.org/all/aiRFcz78QTZdIHHB@arm.com/
Signed-off-by: Tejun Heo <tj@kernel.org>
---
 arch/arm64/include/asm/pgtable.h | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 3ce0f2a6cab6..dc8525431273 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -1838,7 +1838,11 @@ static inline bool ptep_try_set(pte_t *ptep, pte_t new_pte)
 {
 	pteval_t old = 0;
 
-	return try_cmpxchg(&pte_val(*ptep), &old, pte_val(new_pte));
+	if (!try_cmpxchg(&pte_val(*ptep), &old, pte_val(new_pte)))
+		return false;
+
+	__set_pte_complete(new_pte);
+	return true;
 }
 #define ptep_try_set ptep_try_set
 
-- 
2.51.1



^ permalink raw reply related

* Re: [PATCH v3 0/2] KVM: arm64: Sanitise host vCPU fields copied in flush_hyp_vcpu()
From: Fuad Tabba @ 2026-06-07  7:05 UTC (permalink / raw)
  To: Hyunwoo Kim
  Cc: maz, oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
	catalin.marinas, will, linux-arm-kernel, kvmarm, stable
In-Reply-To: <20260606175614.83273-1-imv4bel@gmail.com>

On Sat, 6 Jun 2026 at 18:57, Hyunwoo Kim <imv4bel@gmail.com> wrote:
>
> flush_hyp_vcpu() copies the host vCPU context and vGIC state into the
> hyp's private vCPU on every run. This series sanitises two fields that
> it currently copies verbatim (host -> EL2): __hyp_running_vcpu is
> cleared in the guest context, and used_lrs is bounded by the number of
> implemented list registers.

From my side I think you've addressed Marc's feedback. For the latest series:

Reviewed-by: Fuad Tabba <tabba@google.com>
Tested-by: Fuad Tabba <tabba@google.com>

Cheers,
/fuad

>
> Changes in v3:
> - 2/2: replicate kvm_vgic_global_state.nr_lr into hyp_gicv3_nr_lr
>   once at init (guarded by gicv3_cpuif), instead of reading
>   ICH_VTR_EL2 on every entry behind a gicv3_cpuif gate. (Marc)
> - v2: https://lore.kernel.org/all/20260604151210.1304051-1-imv4bel@gmail.com/
>
> Changes in v2:
> - split into two patches, one per field, per review.
> - v1: https://lore.kernel.org/all/aiFe-CXo-XVTFz1g@v4bel/
>
> Hyunwoo Kim (2):
>   KVM: arm64: Clear __hyp_running_vcpu when flushing the pKVM hyp vCPU
>   KVM: arm64: Bound used_lrs when flushing the pKVM hyp vCPU
>
>  arch/arm64/include/asm/kvm_hyp.h   |  1 +
>  arch/arm64/kvm/arm.c               |  2 ++
>  arch/arm64/kvm/hyp/nvhe/hyp-main.c | 12 ++++++++++++
>  3 files changed, 15 insertions(+)
>
> --
> 2.43.0
>


^ permalink raw reply

* Re: [PATCH] drm/sun4i: fix refcount leak in sun4i_backend_init_sat()
From: Christophe JAILLET @ 2026-06-07  6:32 UTC (permalink / raw)
  To: Wentao Liang, wens, maarten.lankhorst, mripard, tzimmermann,
	airlied, simona, jernej.skrabec, samuel
  Cc: dri-devel, linux-arm-kernel, linux-sunxi, linux-kernel, stable
In-Reply-To: <20260607030950.83636-1-vulab@iscas.ac.cn>

Le 07/06/2026 à 05:09, Wentao Liang a écrit :
> When sun4i_backend_init_sat() calls reset_control_deassert() it
> increments the deassert_count of the reset controller, and must
> pair that with a reset_control_assert() call to decrement it.
> In the error path where clk_prepare_enable() fails, the function
> returns immediately without calling reset_control_assert(), leaking
> the reference count.  Other error paths, like the devm_clk_get()
> failure, correctly jump to the err_assert_reset label which performs
> the missing assert.
> 
> Fix the leak by using the existing err_assert_reset label in the
> clk_prepare_enable error path instead of returning directly.
> 
> Cc: stable@vger.kernel.org
> Fixes: 440d2c7b127a ("drm/sun4i: backend: Handle the SAT")
> Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
> ---
>   drivers/gpu/drm/sun4i/sun4i_backend.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c
> index bc35dad53b07..c9ec5fc26f7e 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_backend.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
> @@ -686,7 +686,7 @@ static int sun4i_backend_init_sat(struct device *dev) {
>   	ret = clk_prepare_enable(backend->sat_clk);
>   	if (ret) {
>   		dev_err(dev, "Couldn't enable the SAT clock\n");
> -		return ret;
> +		goto err_assert_reset;
>   	}
>   
>   	return 0;

Hi,

another way to fix it and simplify the code at the same time would be to 
use devm_reset_control_get_exclusive_deasserted() and 
devm_clk_get_enabled().

just my 2c,

CJ


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox