Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH v4 11/15] arm64: mm: Don't abuse memblock NOMAP to check for overlaps
From: Ard Biesheuvel @ 2026-04-29 14:23 UTC (permalink / raw)
  To: Kevin Brodsky, Ard Biesheuvel, linux-arm-kernel
  Cc: linux-kernel, Will Deacon, Catalin Marinas, Mark Rutland,
	Ryan Roberts, Anshuman Khandual, Liz Prucka, Seth Jenkins,
	Kees Cook, Mike Rapoport, David Hildenbrand, Andrew Morton,
	linux-mm, linux-hardening
In-Reply-To: <8a315ca2-3754-4a66-990e-55b85191aa6e@arm.com>



On Wed, 29 Apr 2026, at 12:54, Kevin Brodsky wrote:
> On 27/04/2026 17:34, Ard Biesheuvel wrote:
>> From: Ard Biesheuvel <ardb@kernel.org>
>>
>> Now that the DRAM mapping routines respect existing table mappings and
>> contiguous block and page mappings, it is no longer needed to fiddle
>> with the memblock tables to set and clear the NOMAP attribute in order
>> to omit text and rodata when creating the linear map.
>>
>> Instead, map the kernel text and rodata alias first with the desired
>> attributes, so that they will not be remapped later with different
>> attributes when mapping the memblocks.
>>
>> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
>> ---
>>  arch/arm64/mm/mmu.c | 24 +++++++-------------
>>  1 file changed, 8 insertions(+), 16 deletions(-)
>>
>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>> index 5e2348b15783..1a4b4337d29a 100644
>> --- a/arch/arm64/mm/mmu.c
>> +++ b/arch/arm64/mm/mmu.c
>> @@ -1148,12 +1148,15 @@ static void __init map_mem(void)
>>  		flags |= NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
>>  
>>  	/*
>> -	 * Take care not to create a writable alias for the
>> -	 * read-only text and rodata sections of the kernel image.
>> -	 * So temporarily mark them as NOMAP to skip mappings in
>> -	 * the following for-loop
>> +	 * Map the linear alias of the [_text, __init_begin) interval
>> +	 * as non-executable now, and remove the write permission in
>> +	 * mark_linear_text_alias_ro() above (which will be called after
>> +	 * alternative patching has completed). This makes the contents
>> +	 * of the region accessible to subsystems such as hibernate,
>> +	 * but protects it from inadvertent modification or execution.
>>  	 */
>> -	memblock_mark_nomap(kernel_start, kernel_end - kernel_start);
>> +	__map_memblock(kernel_start, kernel_end, pgprot_tagged(PAGE_KERNEL),
>
> Is it now necessary for that mapping to be tagged? If so, it should
> probably be called out in the commit message.
>

As you've pointed out, the initial r/w mapping of the linear alias of rodata
will need to be mapped tagged before moving the zero page there. So this patch
will just preserve that, rather than remapping the region tagged explicitly as
it does now.


^ permalink raw reply

* Re: [PATCH 08/43] KVM: arm64: gic-v5: Introduce guest IST alloc and management
From: Marc Zyngier @ 2026-04-29 14:29 UTC (permalink / raw)
  To: Sascha Bischoff
  Cc: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
	kvm@vger.kernel.org, nd, oliver.upton@linux.dev, Joey Gouly,
	Suzuki Poulose, yuzenghui@huawei.com, peter.maydell@linaro.org,
	lpieralisi@kernel.org, Timothy Hayes
In-Reply-To: <20260427160547.3129448-9-sascha.bischoff@arm.com>

On Mon, 27 Apr 2026 17:08:46 +0100,
Sascha Bischoff <Sascha.Bischoff@arm.com> wrote:
> 
> GICv5 guests use Interrupt State Tables (ISTs) to track and manage the
> interrupt state for SPIs and LPIs. These ISTs are provided to the
> host's IRS via the VMTE.
> 
> On a host GICv5 system, SPIs do not require any up-front memory
> allocation prior to their use, unlike LPIs which require the OS to
> allocate an IST. For a GICv5 guest, the same holds from the guest's
> point of view - the SPIs should require no explicit memory allocation
> by the guest. This means that the hypervisor must provision the memory
> which it passed to the IRS for managing a guest's SPI state.
> 
> In light of the above, the hypervisor allocates the SPI IST prior to
> running the guest for the first time. As only a small number of SPIs
> are expected, this is always allocated as a linear IST. The host is
> responsible for freeing this memory on guest teardown.
> 
> For LPIs, the OS needs to provision memory for state tracking. This
> applies to both hosts and guests, and so the guest will provision some
> memory for the LPI IST. However, this is not directly used by
> KVM. Instead, KVM allocates a shadow LPI IST which is passed to the
> IRS (in the VMTE). Again, on guest teardown, the hypervisor must free
> this memory again. The LPI IST is allocated as a two level structure,
> as many more LPIs are expected than SPIs.
> 
> Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
> ---
>  arch/arm64/kvm/vgic/vgic-v5-tables.c | 531 +++++++++++++++++++++++++++
>  arch/arm64/kvm/vgic/vgic-v5-tables.h |  22 ++
>  include/linux/irqchip/arm-gic-v5.h   |   3 +
>  3 files changed, 556 insertions(+)
> 
> diff --git a/arch/arm64/kvm/vgic/vgic-v5-tables.c b/arch/arm64/kvm/vgic/vgic-v5-tables.c
> index 502d05d46cccf..de905f37b61a5 100644
> --- a/arch/arm64/kvm/vgic/vgic-v5-tables.c
> +++ b/arch/arm64/kvm/vgic/vgic-v5-tables.c
> @@ -501,6 +501,25 @@ int vgic_v5_vmte_init(struct kvm *kvm)
>  	return ret;
>  }
>  
> +/*
> + * The following set of forward declarations makes the code layout a *little*
> + * clearer as it lets us keep the IST-related code together.
> + */
> +static int vgic_v5_alloc_linear_ist(struct kvm *kvm, bool spi_ist,
> +				    unsigned int id_bits,
> +				    unsigned int istsz);
> +static int vgic_v5_alloc_l1_ist(struct kvm *kvm, unsigned int id_bits,
> +				unsigned int istsz, unsigned int l2_split);
> +static int vgic_v5_alloc_l2_ists(struct kvm *kvm, unsigned int id_bits,
> +				 unsigned int istsz, unsigned int l2_split);
> +static int vgic_v5_alloc_two_level_lpi_ist(struct kvm *kvm,
> +					   unsigned int id_bits,
> +					   unsigned int istsz,
> +					   unsigned int l2_split);
> +static int vgic_v5_linear_ist_free(struct kvm *kvm, bool spi);
> +static int vgic_v5_two_level_ist_free(struct kvm *kvm, bool spi);
> +static int vgic_v5_spi_ist_free(struct kvm *kvm);
> +
>  /*
>   * Release the VMT Entry, freeing up any allocated data structures before
>   * zeroing the VMTE.
> @@ -531,6 +550,18 @@ int vgic_v5_vmte_release(struct kvm *kvm)
>  	kfree(vmi->vmd_base);
>  	kfree(vmi->vpet_base);
>  
> +	/* If we have an LPI IST, free it */
> +	if (vmi->h_lpi_ist)
> +		ret = vgic_v5_lpi_ist_free(kvm);
> +	if (ret)
> +		return ret;
> +
> +	/* If we have an SPI IST, free it */
> +	if (vmi->h_spi_ist)
> +		ret = vgic_v5_spi_ist_free(kvm);
> +	if (ret)
> +		return ret;
> +
>  	xa_erase(&vm_info, vm_id);
>  	kfree(vmi);
>  
> @@ -634,3 +665,503 @@ int vgic_v5_vmte_free_vpe(struct kvm_vcpu *vcpu)
>  
>  	return 0;
>  }
> +
> +/*
> + * Assign an already allocated IST to the VM by populating the fields in the
> + * corresponding VMTE. We re-use this code for both an SPI IST and LPI IST, even
> + * if the paths to reach it might be vastly different.
> + */
> +int vgic_v5_vmte_assign_ist(struct kvm *kvm, phys_addr_t ist_base,
> +			    bool two_level, unsigned int id_bits,
> +			    unsigned int l2sz, unsigned int istsz,
> +			    bool spi_ist)
> +{
> +	struct kvm_vcpu *vcpu0 = kvm_get_vcpu(kvm, 0);
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct gicv5_cmd_info cmd_info;
> +	struct vmtl2_entry *vmte;
> +	unsigned int section;
> +	u64 tmp;
> +	int ret;
> +
> +	section = spi_ist ? GICV5_VMTEL2_SPI_SECTION : GICV5_VMTEL2_LPI_SECTION;

Section? What is a section? This needs documentation (11.2.2 in the
EAC0 version of the spec) so that people can understand you are
talking about the 64bit word number in the Level-2 VM Table Entry.

> +
> +	if (ist_base & ~GICV5_VMTEL2E_IST_ADDR) {
> +		kvm_err("IST alignment issue! Address: 0x%llx, Mask 0x%llx\n",
> +			ist_base, GICV5_VMTEL2E_IST_ADDR);
> +		return -EINVAL;
> +	}
> +
> +	ret = vgic_v5_get_l2_vmte(vm_id, &vmte);
> +	if (ret)
> +		return ret;
> +
> +	/* Bail if already allocated - something is broken! */
> +	if (FIELD_GET(GICV5_VMTEL2E_IST_VALID, vmte->val[section])) {
> +		vgic_v5_clean_inval(vmte, sizeof(*vmte), true, true);

Still this odd construct. I'm starting to wonder whether I'm really
missing something.

> +		return -EINVAL;
> +	}
> +
> +	tmp = FIELD_PREP(GICV5_VMTEL2E_IST_L2SZ, l2sz);
> +	tmp |= FIELD_PREP(GICV5_VMTEL2E_IST_ADDR,
> +			ist_base >> GICV5_VMTEL2E_IST_ADDR_SHIFT);
> +	tmp |= FIELD_PREP(GICV5_VMTEL2E_IST_ISTSZ, istsz);
> +	tmp |= FIELD_PREP(GICV5_VMTEL2E_IST_ID_BITS, id_bits);
> +	tmp |= FIELD_PREP(GICV5_VMTEL2E_IST_STRUCTURE, two_level);
> +
> +	WRITE_ONCE(vmte->val[section], cpu_to_le64(tmp));
> +	vgic_v5_clean_inval(vmte, sizeof(*vmte), true, false);
> +
> +	/* Finally, mark the entry as valid */
> +	cmd_info.cmd_type = spi_ist ? SPI_VIST_MAKE_VALID : LPI_VIST_MAKE_VALID;
> +	ret = irq_set_vcpu_affinity(vgic_v5_vpe_db(vcpu0), &cmd_info);
> +
> +	/* Any cached entries we now have are stale! */
> +	vgic_v5_clean_inval(vmte, sizeof(*vmte), false, true);

Shouldn't the clean operation happen *before* you call into the IRQ
stack? It feels dangerous to do so, even if the callback doesn't do
much.

> +
> +	return ret;
> +}
> +
> +/*
> + * Helper to determine the correct l2sz to use based on the combination of
> + * PAGE_SIZE and whatever hardware supports.
> + */
> +static unsigned int vgic_v5_ist_l2sz(void)
> +{
> +	switch (PAGE_SIZE) {
> +	case SZ_64K:
> +		if (gicv5_host_ist_caps.ist_l2sz & 0x4)

Please had definitions for IRS_IDR2.IST_L2SZ.

> +			return GICV5_IRS_IST_CFGR_L2SZ_64K;
> +		fallthrough;
> +	case SZ_4K:
> +		if (gicv5_host_ist_caps.ist_l2sz & 0x1)
> +			return GICV5_IRS_IST_CFGR_L2SZ_4K;
> +		fallthrough;
> +	case SZ_16K:
> +		if (gicv5_host_ist_caps.ist_l2sz & 0x2)
> +			return GICV5_IRS_IST_CFGR_L2SZ_16K;
> +		break;
> +	}
> +
> +	if (gicv5_host_ist_caps.ist_l2sz & 0x1)
> +		return GICV5_IRS_IST_CFGR_L2SZ_4K;
> +
> +	return GICV5_IRS_IST_CFGR_L2SZ_64K;
> +}
> +
> +/* Helper to determine ISTE size based on metadata requirements */
> +static unsigned int vgic_v5_ist_istsz(unsigned int id_bits)
> +{
> +	if (!gicv5_host_ist_caps.istmd)
> +		return GICV5_IRS_IST_CFGR_ISTSZ_4;
> +
> +	if (id_bits >= gicv5_host_ist_caps.istmd_sz)
> +		return GICV5_IRS_IST_CFGR_ISTSZ_16;
> +
> +	return GICV5_IRS_IST_CFGR_ISTSZ_8;
> +}
> +
> +/*
> + * Allocate a Linear IST - always used for SPIs and potentially LPIs.
> + *
> + * The calculation for n has been taken from the GICv5 spec.

Bonus points if you add a reference to the relevant part of the spec.

> + *
> + * NOTE: istsz is the FIELD used by GICv5, not the actual size (or log2() of the
> + * size).
> + */
> +static int vgic_v5_alloc_linear_ist(struct kvm *kvm, bool spi_ist,
> +				    unsigned int id_bits, unsigned int istsz)
> +{
> +	const size_t n = id_bits + 1 + istsz;
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct vgic_v5_vm_info *vmi;
> +	__le64 *ist;
> +	u32 l1sz;
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (WARN_ON_ONCE(!vmi))
> +		return -EINVAL;
> +
> +	/*
> +	 * Allocate the IST. We only have one level, so we just use the L2 ISTE.
> +	 */
> +	l1sz = BIT(n + 1);
> +	ist = kzalloc(l1sz, GFP_KERNEL);
> +	if (!ist)
> +		return -ENOMEM;
> +
> +	if (spi_ist) {
> +		vmi->h_spi_ist = ist;
> +	} else {
> +		vmi->h_lpi_ist_structure = false;
> +		vmi->h_lpi_ist = ist;
> +	}
> +
> +	vgic_v5_clean_inval(ist, l1sz, true, true);
> +
> +	return 0;
> +}
> +
> +/*
> + * Allocate the first level of a two-level IST - LPI, only.
> + *
> + * The calculations for n, l1_size have been taken from the GICv5 spec.
> + *
> + * NOTE: istsz and l2sz are the FIELDS used by GICv5, not the actual sizes (or
> + * log2() of the sizes).
> + */
> +static int vgic_v5_alloc_l1_ist(struct kvm *kvm, unsigned int id_bits,
> +				unsigned int istsz, unsigned int l2sz)
> +{
> +	const size_t n =  max(5, id_bits - ((10 - istsz) + (2 * l2sz)) + 3 - 1);
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	const u32 l1_size = BIT(n + 1);
> +	struct vgic_v5_vm_info *vmi;
> +	__le64 *ist;
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (!vmi)
> +		return -EINVAL;
> +
> +	ist = kzalloc(l1_size, GFP_KERNEL);
> +	if (!ist)
> +		return -ENOMEM;
> +
> +	vmi->h_lpi_ist_structure = true;
> +	vmi->h_lpi_ist = ist;
> +
> +	vgic_v5_clean_inval(ist, l1_size, true, true);
> +
> +	return 0;
> +}
> +
> +/*
> + * Allocate ALL of the second level ISTs for a two-level IST - LPI, only.
> + *
> + * The calculations for n, l1_entries, l2_size have been taken from the GICv5
> + * spec.
> + *
> + * NOTE: istsz and l2sz are the FIELDS used by GICv5, not the actual sizes (or
> + * log2() of the sizes).
> + */
> +static int vgic_v5_alloc_l2_ists(struct kvm *kvm, unsigned int id_bits,
> +				unsigned int istsz, unsigned int l2sz)
> +{
> +	const size_t n =  max(5, id_bits - ((10 - istsz) + (2 * l2sz)) + 3 - 1);
> +	const int l1_entries = BIT(n + 1) / GICV5_IRS_ISTL1E_SIZE;
> +	const size_t l2_size = BIT(11 + (2 * l2sz) + 1);
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct vgic_v5_vm_info *vmi;
> +	__le64 *l2ist;
> +	__le64 *l1ist;
> +	int index;
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (WARN_ON_ONCE(!vmi))
> +		return -EINVAL;
> +
> +	l1ist = vmi->h_lpi_ist;
> +
> +	/*
> +	 * Allocate the storage for the pointers to the L2 ISTs (used when
> +	 * freeing later).
> +	 */
> +	vmi->h_lpi_l2_ists = kzalloc_objs(*vmi->h_lpi_l2_ists, l1_entries,
> +					  GFP_KERNEL);
> +	if (!vmi->h_lpi_l2_ists)
> +		return -ENOMEM;
> +
> +	/* Allocate the L2 IST for each L1 IST entry */
> +	for (index = 0; index < l1_entries; ++index) {
> +		l2ist = kzalloc(l2_size, GFP_KERNEL);
> +		if (!l2ist) {
> +			while (--index >= 0)
> +				kfree(vmi->h_lpi_l2_ists[index]);
> +
> +			kfree(vmi->h_lpi_l2_ists);
> +			vmi->h_lpi_l2_ists = NULL;
> +
> +			return -ENOMEM;
> +		}
> +
> +		/*
> +		 * We are not doing on-demand allocation of the L2 ISTs, and are
> +		 * instead provisioning the whole IST up front. This means that
> +		 * we are able to mark the L2 ISTs as valid in the L1 ISTEs as
> +		 * the overall IST is not yet valid.
> +		 */
> +		l1ist[index] = cpu_to_le64(
> +			virt_to_phys(l2ist) & GICV5_ISTL1E_L2_ADDR_MASK) |
> +			GICV5_ISTL1E_VALID;
> +
> +		vmi->h_lpi_l2_ists[index] = l2ist;
> +
> +		vgic_v5_clean_inval(l2ist, l2_size, true, true);
> +	}
> +
> +	/* Handle CMOs for the whole L1 IST in one go */
> +	vgic_v5_clean_inval(l1ist, l1_entries * sizeof(*l1ist), true, false);
> +
> +	return 0;
> +}
> +
> +/* Allocate a two-level IST - LPIs, only */
> +static int vgic_v5_alloc_two_level_lpi_ist(struct kvm *kvm, unsigned int id_bits,
> +					   unsigned int istsz, unsigned int l2sz)
> +{
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct vgic_v5_vm_info *vmi;
> +	int ret;
> +
> +	/*
> +	 * Allocate the L1 IST first, then all of the L2s. Everything
> +	 * is preallocated and we do no on-demand IST allocation. This
> +	 * is to avoid needing to track if and when the guest is doing
> +	 * on-demand IST allocation.
> +	 */
> +	ret = vgic_v5_alloc_l1_ist(kvm, id_bits, istsz, l2sz);
> +	if (ret)
> +		return ret;
> +
> +	ret = vgic_v5_alloc_l2_ists(kvm, id_bits, istsz, l2sz);
> +	if (ret) {
> +		/* Free the L1 IST again */
> +		vmi = xa_load(&vm_info, vm_id);
> +		kfree(vmi->h_lpi_ist);
> +		vmi->h_lpi_ist = 0;
> +
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vgic_v5_free_allocated_lpi_ist(struct vgic_v5_vm_info *vmi,
> +					   unsigned int id_bits,
> +					   unsigned int istsz,
> +					   unsigned int l2sz)
> +{
> +	if (!vmi->h_lpi_ist_structure) {
> +		kfree(vmi->h_lpi_ist);
> +		vmi->h_lpi_ist = NULL;
> +		return;
> +	}
> +
> +	if (vmi->h_lpi_l2_ists) {
> +		const size_t n = max(2, id_bits - ((10 - istsz) + (2 * l2sz)) + 3 - 1);
> +		const int l1_entries = BIT(n + 1) / GICV5_IRS_ISTL1E_SIZE;
> +		int index;
> +
> +		for (index = 0; index < l1_entries; ++index)
> +			kfree(vmi->h_lpi_l2_ists[index]);
> +
> +		kfree(vmi->h_lpi_l2_ists);
> +		vmi->h_lpi_l2_ists = NULL;
> +	}
> +
> +	kfree(vmi->h_lpi_ist);
> +	vmi->h_lpi_ist = NULL;
> +}
> +
> +void vgic_v5_free_allocated_spi_ist(struct kvm *kvm)
> +{
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct vgic_v5_vm_info *vmi;
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (WARN_ON_ONCE(!vmi))
> +		return;
> +
> +	kfree(vmi->h_spi_ist);
> +	vmi->h_spi_ist = NULL;
> +}
> +
> +/*
> + * Free a Linear IST. Can only happen once the VM is dead.
> + */
> +static int vgic_v5_linear_ist_free(struct kvm *kvm, bool spi)
> +{
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct vmtl2_entry *vmte;
> +	struct vgic_v5_vm_info *vmi;
> +	int section, ret;
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (!vmi)
> +		return -EINVAL;
> +
> +	ret = vgic_v5_get_l2_vmte(vm_id, &vmte);
> +	if (ret)
> +		return ret;
> +
> +	if (spi) {
> +		section = GICV5_VMTEL2_SPI_SECTION;
> +		vgic_v5_free_allocated_spi_ist(kvm);
> +	} else {
> +		section = GICV5_VMTEL2_LPI_SECTION;
> +		vgic_v5_free_allocated_lpi_ist(vmi, 0, 0, 0);
> +	}
> +
> +	/* The VM should be dead here, so we can just zero the VMT section */
> +	WRITE_ONCE(vmte->val[section], 0ULL);
> +	vgic_v5_clean_inval(vmte, sizeof(*vmte), true, true);
> +
> +	return 0;
> +}
> +
> +/*
> + * Free a Two-Level IST. Can only happen once the VM is dead.
> + */
> +static int vgic_v5_two_level_ist_free(struct kvm *kvm, bool spi)
> +{
> +	unsigned int id_bits, istsz, l2sz;
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct vgic_v5_vm_info *vmi;
> +	__le64 *l1ist, tmp;
> +	struct vmtl2_entry *vmte;
> +	int section, l1_entries;
> +	size_t n;
> +	int ret;
> +
> +	/* We don't create two-level SPI ISTs, so freeing is a bad idea! */
> +	if (spi)
> +		return -EINVAL;
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (!vmi)
> +		return -EINVAL;
> +
> +	section = GICV5_VMTEL2_LPI_SECTION;
> +	l1ist = vmi->h_lpi_ist;
> +
> +	if (!vmi->h_lpi_ist_structure)
> +		return -EINVAL;
> +
> +	ret = vgic_v5_get_l2_vmte(vm_id, &vmte);
> +	if (ret)
> +		return ret;
> +
> +	tmp = le64_to_cpu(READ_ONCE(vmte->val[section]));
> +
> +	id_bits = FIELD_GET(GICV5_VMTEL2E_IST_ID_BITS, tmp);
> +	istsz = FIELD_GET(GICV5_VMTEL2E_IST_ISTSZ, tmp);
> +	l2sz = FIELD_GET(GICV5_VMTEL2E_IST_L2SZ, tmp);
> +
> +	/* Calculation for n taken from the GICv5 specification */
> +	n =  max(2, id_bits - ((10 - istsz) + (2 * l2sz)) + 3 - 1);
> +	l1_entries = BIT(n + 1) / GICV5_IRS_ISTL1E_SIZE;
> +
> +	vgic_v5_free_allocated_lpi_ist(vmi, id_bits, istsz, l2sz);
> +
> +	/* The VM must be dead, so we can just zero the VMT section */
> +	WRITE_ONCE(vmte->val[section], 0ULL);
> +
> +	vgic_v5_clean_inval(vmte, sizeof(*vmte), true, true);
> +
> +	return 0;
> +}
> +
> +/*
> + * Allocate an IST for SPIs.
> + *
> + * We don't anticipate a large number of SPIs being allocated. Therefore, we
> + * always allocate a Linear IST for SPIs. This will need to be revisited should
> + * that assumption no longer hold.
> + */
> +int vgic_v5_spi_ist_allocate(struct kvm *kvm, phys_addr_t *base_addr,
> +			     unsigned int id_bits, unsigned int istsz)
> +{
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct vgic_v5_vm_info *vmi;
> +	int ret;
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (WARN_ON_ONCE(!vmi))
> +		return -EINVAL;
> +
> +	ret = vgic_v5_alloc_linear_ist(kvm, true, id_bits, istsz);
> +	if (ret)
> +		return ret;
> +
> +	*base_addr = virt_to_phys(vmi->h_spi_ist);
> +
> +	return 0;
> +}
> +
> +/*
> + * Free the IST for SPIs. Should only happen once the VM is dead.
> + */
> +static int vgic_v5_spi_ist_free(struct kvm *kvm)
> +{
> +	return vgic_v5_linear_ist_free(kvm, true);
> +}
> +
> +/*
> + * Allocate an IST for LPIs.
> + *
> + * Unlike with SPIs, we anticipate that the guest will allocate a relatively
> + * large number of LPIs. Therefore, while we support doing a linear LPI IST, it
> + * is expected that LPI ISTs will be two-level.
> + */
> +int vgic_v5_lpi_ist_alloc(struct kvm *kvm, unsigned int id_bits)
> +{
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct vgic_v5_vm_info *vmi;
> +	unsigned int istsz, l2sz;
> +	phys_addr_t phys_addr;
> +	bool two_level;
> +	int ret;
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (WARN_ON_ONCE(!vmi))
> +		return -EINVAL;
> +
> +	istsz = vgic_v5_ist_istsz(id_bits);
> +	l2sz = vgic_v5_ist_l2sz();
> +
> +	/*
> +	 * Determine if we want to create a Linear or a Two-Level IST.
> +	 *
> +	 * If we require more than one page for the IST, create a Two-Level IST
> +	 * (if the host supports it, which is likely).
> +	 *
> +	 * Note: GICv5's istsz is not the size of the ISTEs in log2(bytes). It
> +	 * is 2 less, hence the +2 below.
> +	 */
> +	two_level = gicv5_host_ist_caps.ist_levels &&
> +		id_bits > PAGE_SHIFT - (2 + istsz);
> +
> +	if (!two_level)
> +		ret = vgic_v5_alloc_linear_ist(kvm, false /* LPIs, not SPIs */,
> +					       id_bits, istsz);
> +	else
> +		ret = vgic_v5_alloc_two_level_lpi_ist(kvm, id_bits, istsz,
> +						      l2sz);
> +
> +	if (ret)
> +		return ret;
> +
> +	phys_addr = virt_to_phys(vmi->h_lpi_ist);
> +	ret = vgic_v5_vmte_assign_ist(kvm, phys_addr, two_level, id_bits, l2sz,
> +				      istsz, false);
> +	if (ret)
> +		vgic_v5_free_allocated_lpi_ist(vmi, id_bits, istsz, l2sz);
> +
> +	return ret;
> +}
> +
> +/* Free the LPI IST again */
> +int vgic_v5_lpi_ist_free(struct kvm *kvm)
> +{
> +	u16 vm_id = vgic_v5_vm_id(kvm);
> +	struct vgic_v5_vm_info *vmi;
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (!vmi)
> +		return -ENXIO;
> +
> +	if (!vmi->h_lpi_ist_structure)
> +		return vgic_v5_linear_ist_free(kvm, false);
> +	else
> +		return vgic_v5_two_level_ist_free(kvm, false);
> +}
> diff --git a/arch/arm64/kvm/vgic/vgic-v5-tables.h b/arch/arm64/kvm/vgic/vgic-v5-tables.h
> index 5501a44308362..37e220cda1987 100644
> --- a/arch/arm64/kvm/vgic/vgic-v5-tables.h
> +++ b/arch/arm64/kvm/vgic/vgic-v5-tables.h
> @@ -54,6 +54,13 @@ struct vmtl2_entry {
>  #define GICV5_VMTEL2E_IST_STRUCTURE	BIT_ULL(58)
>  #define GICV5_VMTEL2E_IST_ID_BITS	GENMASK_ULL(63, 59)
>  
> +/*
> + * The LPI and SPI configuration is stored in the 2nd and 3rd 64-bit chunks of
> + * the VMTE (0-based).
> + */
> +#define GICV5_VMTEL2_LPI_SECTION	2
> +#define GICV5_VMTEL2_SPI_SECTION	3
> +
>  /* Virtual PE Table Entry */
>  typedef __le64 vpe_entry;
>  #define GICV5_VPE_VALID			BIT_ULL(0)
> @@ -66,6 +73,12 @@ struct vgic_v5_vm_info {
>  	vpe_entry __iomem	*vpet_base;
>  	void __iomem		**vped_ptrs;
>  	u8			vpe_id_bits;
> +
> +	/* Tracking for the hyp-owned ISTs */
> +	bool			h_lpi_ist_structure;
> +	__le64			*h_lpi_ist;
> +	__le64			**h_lpi_l2_ists;
> +	__le64			*h_spi_ist;

Can you please document what these individual fields represent? I'm
not sure what hyp-owned means here...

>  };
>  
>  struct vgic_v5_vmt {
> @@ -146,4 +159,13 @@ int vgic_v5_vmte_release(struct kvm *kvm);
>  int vgic_v5_vmte_alloc_vpe(struct kvm_vcpu *vcpu);
>  int vgic_v5_vmte_free_vpe(struct kvm_vcpu *vcpu);
>  
> +int vgic_v5_vmte_assign_ist(struct kvm *kvm, phys_addr_t ist_base,
> +			    bool two_level, unsigned int id_bits,
> +			    unsigned int l2sz, unsigned int istsz, bool spi_ist);
> +int vgic_v5_spi_ist_allocate(struct kvm *kvm, phys_addr_t *base_addr,
> +			     unsigned int id_bits, unsigned int istsz);
> +void vgic_v5_free_allocated_spi_ist(struct kvm *kvm);
> +int vgic_v5_lpi_ist_alloc(struct kvm *kvm, unsigned int id_bits);
> +int vgic_v5_lpi_ist_free(struct kvm *kvm);
> +
>  #endif
> diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h
> index 89579ee04f5d1..ccec0a045927c 100644
> --- a/include/linux/irqchip/arm-gic-v5.h
> +++ b/include/linux/irqchip/arm-gic-v5.h
> @@ -450,6 +450,9 @@ enum gicv5_vcpu_info_cmd_type {
>  	VMT_L2_MAP,		/* Map in a L2 VMT - *may* happen on VM init */
>  	VMTE_MAKE_VALID,	/* Make the VMTE valid */
>  	VMTE_MAKE_INVALID,	/* Make the VMTE (et al.) invalid */
> +	SPI_VIST_MAKE_VALID,	/* No corresponding invalid */
> +	LPI_VIST_MAKE_VALID,	/* Triggered by a guest */
> +	LPI_VIST_MAKE_INVALID,	/* Triggered by a guest */
>  };
>  
>  struct gicv5_cmd_info {

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.


^ permalink raw reply

* [PATCH v3] kselftest/arm64: Include <asm/ptrace.h> for user_gcs definition
From: Leo Yan @ 2026-04-29 14:30 UTC (permalink / raw)
  To: Catalin Marinas, Will Deacon, Shuah Khan, Mark Brown,
	Thiago Jung Bauermann
  Cc: linux-arm-kernel, linux-kselftest, linux-kernel, Leo Yan

kselftest includes kernel uAPI headers with option:

  -isystem $(top_srcdir)/usr/include

Include <asm/ptrace.h> in libc-gcs.c for the definition of struct
user_gcs from the uAPI headers, and remove the redundant
definition in gcs-util.h.

Fixes: a505a52b4e29 ("kselftest/arm64: Add a GCS test program built with the system libc")
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
Changes in v3:
- Moved to include <asm/ptrace.h> in libc-gcs.c (Catalin).
- Removed the redundant definition in gcs-util.h (Catalin).
- Link to v2: https://lore.kernel.org/r/20260422-selftests_arm64_gcc15-v2-1-c0134de8838a@arm.com

Changes in v2:
- Refined commit log (Mark Brown).
- Moving header to gcs-util.h (Mark Brown).
- Link to v1: https://lore.kernel.org/r/20260422-selftests_arm64_gcc15-v1-1-0d919ea5ac5f@arm.com
---
 tools/testing/selftests/arm64/gcs/gcs-util.h | 6 ------
 tools/testing/selftests/arm64/gcs/libc-gcs.c | 1 +
 2 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/tools/testing/selftests/arm64/gcs/gcs-util.h b/tools/testing/selftests/arm64/gcs/gcs-util.h
index c99a6b39ac147b4efbc9b5fbadb43daf4da2c85e..7a81bb07ed4b8b82ee90d93b336b26760703d55b 100644
--- a/tools/testing/selftests/arm64/gcs/gcs-util.h
+++ b/tools/testing/selftests/arm64/gcs/gcs-util.h
@@ -18,12 +18,6 @@
 
 #ifndef NT_ARM_GCS
 #define NT_ARM_GCS 0x410
-
-struct user_gcs {
-	__u64 features_enabled;
-	__u64 features_locked;
-	__u64 gcspr_el0;
-};
 #endif
 
 /* Shadow Stack/Guarded Control Stack interface */
diff --git a/tools/testing/selftests/arm64/gcs/libc-gcs.c b/tools/testing/selftests/arm64/gcs/libc-gcs.c
index 17b2fabfec386e523e788d8bcfcafa553c93bd9b..72e82bfbecc99e34da71d56cf5401c137d6e9a48 100644
--- a/tools/testing/selftests/arm64/gcs/libc-gcs.c
+++ b/tools/testing/selftests/arm64/gcs/libc-gcs.c
@@ -16,6 +16,7 @@
 
 #include <asm/hwcap.h>
 #include <asm/mman.h>
+#include <asm/ptrace.h>
 
 #include <linux/compiler.h>
 

---
base-commit: 4ee64205ffaa587e8114d84a67ac721399ccb369
change-id: 20260421-selftests_arm64_gcc15-15f6f6d07dd8

Best regards,
-- 
Leo Yan <leo.yan@arm.com>



^ permalink raw reply related

* Re: [PATCH v4 11/15] arm64: mm: Don't abuse memblock NOMAP to check for overlaps
From: Kevin Brodsky @ 2026-04-29 14:30 UTC (permalink / raw)
  To: Ard Biesheuvel, Ard Biesheuvel, linux-arm-kernel
  Cc: linux-kernel, Will Deacon, Catalin Marinas, Mark Rutland,
	Ryan Roberts, Anshuman Khandual, Liz Prucka, Seth Jenkins,
	Kees Cook, Mike Rapoport, David Hildenbrand, Andrew Morton,
	linux-mm, linux-hardening
In-Reply-To: <1f9c1ca5-c15c-4a08-aa8c-cbe64f7f205b@app.fastmail.com>

On 29/04/2026 16:23, Ard Biesheuvel wrote:
> On Wed, 29 Apr 2026, at 12:54, Kevin Brodsky wrote:
>> On 27/04/2026 17:34, Ard Biesheuvel wrote:
>>> From: Ard Biesheuvel <ardb@kernel.org>
>>>
>>> Now that the DRAM mapping routines respect existing table mappings and
>>> contiguous block and page mappings, it is no longer needed to fiddle
>>> with the memblock tables to set and clear the NOMAP attribute in order
>>> to omit text and rodata when creating the linear map.
>>>
>>> Instead, map the kernel text and rodata alias first with the desired
>>> attributes, so that they will not be remapped later with different
>>> attributes when mapping the memblocks.
>>>
>>> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
>>> ---
>>>  arch/arm64/mm/mmu.c | 24 +++++++-------------
>>>  1 file changed, 8 insertions(+), 16 deletions(-)
>>>
>>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>>> index 5e2348b15783..1a4b4337d29a 100644
>>> --- a/arch/arm64/mm/mmu.c
>>> +++ b/arch/arm64/mm/mmu.c
>>> @@ -1148,12 +1148,15 @@ static void __init map_mem(void)
>>>  		flags |= NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
>>>  
>>>  	/*
>>> -	 * Take care not to create a writable alias for the
>>> -	 * read-only text and rodata sections of the kernel image.
>>> -	 * So temporarily mark them as NOMAP to skip mappings in
>>> -	 * the following for-loop
>>> +	 * Map the linear alias of the [_text, __init_begin) interval
>>> +	 * as non-executable now, and remove the write permission in
>>> +	 * mark_linear_text_alias_ro() above (which will be called after
>>> +	 * alternative patching has completed). This makes the contents
>>> +	 * of the region accessible to subsystems such as hibernate,
>>> +	 * but protects it from inadvertent modification or execution.
>>>  	 */
>>> -	memblock_mark_nomap(kernel_start, kernel_end - kernel_start);
>>> +	__map_memblock(kernel_start, kernel_end, pgprot_tagged(PAGE_KERNEL),
>> Is it now necessary for that mapping to be tagged? If so, it should
>> probably be called out in the commit message.
>>
> As you've pointed out, the initial r/w mapping of the linear alias of rodata
> will need to be mapped tagged before moving the zero page there. So this patch
> will just preserve that, rather than remapping the region tagged explicitly as
> it does now.

Right! I got lost in all the mappings :)

- Kevin


^ permalink raw reply

* Re: [PATCH v3 3/3] ARM: at91: remove unnecessary of_platform_default_populate calls
From: Miquel Raynal @ 2026-04-29 14:31 UTC (permalink / raw)
  To: Rob Herring (Arm)
  Cc: Richard Weinberger, Vignesh Raghavendra, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Russell King, linux-mtd,
	linux-arm-kernel, linux-kernel
In-Reply-To: <20260429-blazing-thus-c1a4eb9b6b0c@thorsis.com>

On 29/04/2026 at 15:02:22 +02, Alexander Dahl <ada@thorsis.com> wrote:

> Hello Miquel,
>
> Am Wed, Apr 29, 2026 at 11:42:19AM +0200 schrieb Miquel Raynal:
>> Hi Alexander,
>> 
>> >     [    0.958298] bus: 'platform': add driver atmel-ebi
>> >     [    0.958451] platform 10000000.ebi: bus: 'platform': __driver_probe_device: matched device with driver atmel-ebi
>> >     [    0.958566] platform 10000000.ebi: error -EPROBE_DEFER: wait for supplier /ahb/apb/pinctrl@fffff400/ebi/ebi-data-lsb
>> >     [    0.958649] platform 10000000.ebi: Added to deferred list
>> >     [    0.959429] bus: 'platform': remove driver atmel-ebi
>> >     [    0.959540] driver: 'atmel-ebi': driver_release
>> 
>> Interesting.
>> 
>> If you look at the very last line of the driver, the EBI driver does not
>> use a standard "module_platform_driver" macro, it uses some kind of
>> run-once probe that is kept in an __init section:
>> https://elixir.bootlin.com/linux/v7.0.1/source/drivers/memory/atmel-ebi.c#L637
>> 
>> And this seems to be incompatible with probe deferrals:
>> https://elixir.bootlin.com/linux/v7.0.1/source/drivers/base/platform.c#L948
>
> Indeed. o.O
>
>> I would suggest trying something alone these lines:
>> 
>> --- a/drivers/memory/atmel-ebi.c
>> +++ b/drivers/memory/atmel-ebi.c
>> @@ -633,5 +633,6 @@ static struct platform_driver atmel_ebi_driver = {
>>                 .of_match_table = atmel_ebi_id_table,
>>                 .pm = &atmel_ebi_pm_ops,
>>         },
>> +       .probe = atmel_ebi_probe,
>>  };
>> -builtin_platform_driver_probe(atmel_ebi_driver, atmel_ebi_probe);
>> +builtin_platform_driver(atmel_ebi_driver);
>> 
>> Memory taken by the probe function will remain mapped. That seems to be
>> the drawback.
>
> This works.  Sent a patch, see:
>
> https://lore.kernel.org/all/20260429125930.844790-1-ada@thorsis.com/
>
> Regarding the drawback: This can't be much memory, can it?

I guess it is preferable to have the probe mapped at the cost of a few
hundred more RAM bytes than a brick...

Good to know that this fixed your problem.

Thanks,
Miquèl


^ permalink raw reply

* Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
From: Padhi, Beleswar @ 2026-04-29 14:35 UTC (permalink / raw)
  To: Shenwei Wang, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Mathieu Poirier, Frank Li, Sascha Hauer
  Cc: Shuah Khan, linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org, Pengutronix Kernel Team,
	Fabio Estevam, Peng Fan, devicetree@vger.kernel.org,
	linux-remoteproc@vger.kernel.org, imx@lists.linux.dev,
	linux-arm-kernel@lists.infradead.org, dl-linux-imx,
	Bartosz Golaszewski, Andrew Lunn
In-Reply-To: <PAXPR04MB91858D4F2549FA068D7ECCC289372@PAXPR04MB9185.eurprd04.prod.outlook.com>


On 4/28/2026 10:06 PM, Shenwei Wang wrote:
>
>> -----Original Message-----
>> From: Padhi, Beleswar <b-padhi@ti.com>
>> Sent: Tuesday, April 28, 2026 10:53 AM
>> To: Shenwei Wang <shenwei.wang@nxp.com>; Linus Walleij
>> <linusw@kernel.org>; Bartosz Golaszewski <brgl@kernel.org>; Jonathan Corbet
>> <corbet@lwn.net>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski
>> <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; Bjorn Andersson
>> <andersson@kernel.org>; Mathieu Poirier <mathieu.poirier@linaro.org>; Frank Li
>> <frank.li@nxp.com>; Sascha Hauer <s.hauer@pengutronix.de>
>> Cc: Shuah Khan <skhan@linuxfoundation.org>; linux-gpio@vger.kernel.org; linux-
>> doc@vger.kernel.org; linux-kernel@vger.kernel.org; Pengutronix Kernel Team
>> <kernel@pengutronix.de>; Fabio Estevam <festevam@gmail.com>; Peng Fan
>> <peng.fan@nxp.com>; devicetree@vger.kernel.org; linux-
>> remoteproc@vger.kernel.org; imx@lists.linux.dev; linux-arm-
>> kernel@lists.infradead.org; dl-linux-imx <linux-imx@nxp.com>; Bartosz
>> Golaszewski <brgl@bgdev.pl>; Andrew Lunn <andrew@lunn.ch>
>> Subject: [EXT] Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
>>>> Nothing extra in my opinion. rpmsg_create_ept() just creates a
>>>> dynamic local endpoint address for Linux's usage. The firmware just
>>>> has to make sure to reply to the same endpoint address where it
>>>> received the message. This should already be in place IMO, because
>>>> currently you are sending all messages in the default
>>> Since rpmsg_create_ept creates a new local endpoint address on the
>>> Linux side, how is the remote system expected to learn and use this
>>> new address for communication if no additional logic is added on the remote
>> side?
>>
>>
>> Remote side learns the endpoint when it receives any message from Linux from
>> the dynamic endpoint.
>>
>> Lets say rpmsg_create_ept() allocates a dynamic local ept of 1026. When you
>> send the message from this endpoint, the standard rpmsg header would have:
>>
>>       85 struct rpmsg_hdr {
>>       86         __rpmsg32 src; // 1026
>>       87         __rpmsg32 dst; // rpdev->dst (e.g. 400)
>>       88         __rpmsg32 reserved;
>>       89         __rpmsg16 len;
>>       90         __rpmsg16 flags;
>>       91         u8 data[];
>>       92 } __packed;
>>
>> Remote side tracks the dynamic endpoint by reading src = 1026. And while
>> sending the response it fills the header as:
>>
>>       85 struct rpmsg_hdr {
>>       86         __rpmsg32 src; // 400
>>       87         __rpmsg32 dst; // 1026
>>       88         __rpmsg32 reserved;
>>       89         __rpmsg16 len;
>>       90         __rpmsg16 flags;
>>       91         u8 data[];
>>       92 } __packed;
>>
> This explains how reply messages work in this scenario: the remote side can simply send
> the response back to the source address of the incoming message.
>
> How does this work for notification messages initiated by the remote side? Should the remote
> system need to add additional logic to track the source address based on the GPIO instance?


You should already have the tracking logic in firmware. How else are you
sending the notification messages from firmware with your current v13
implementation? Are you assuming the channel address to be always the
same? If so, this is a bug and should be fixed in firmware because the
address is generated dynamically. For example, if another core announces
its name service first, then the channel address for your core would be
different and the functionality would break.

Instead, you should have a map of ept to port idx in the firmware side
when you receive a message from Linux (just like we would maintain it
in struct rpmsg_gpio_port in Linux too).

Thanks,
Beleswar

[...]



^ permalink raw reply

* Re: [PATCH v21 05/13] mfd: psci-mfd: Add PSCI MFD driver for cpuidle-psci-domain cell
From: Pankaj Patil @ 2026-04-29 14:40 UTC (permalink / raw)
  To: Shivendra Pratap, Sebastian Reichel, Mark Rutland,
	Lorenzo Pieralisi, Rafael J. Wysocki, Daniel Lezcano,
	Christian Loehle, Ulf Hansson, Lee Jones, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
	Arnd Bergmann, Souvik Chakravarty, Andy Yan, Matthias Brugger,
	John Stultz, Moritz Fischer, Bartosz Golaszewski, Sudeep Holla
  Cc: linux-pm, linux-kernel, linux-arm-msm, linux-arm-kernel,
	devicetree, Florian Fainelli, Krzysztof Kozlowski,
	Dmitry Baryshkov, Mukesh Ojha, Andre Draszik, Greg Kroah-Hartman,
	Kathiravan Thirumoorthy, Srinivas Kandagatla
In-Reply-To: <20260427-arm-psci-system_reset2-vendor-reboots-v21-5-dcf937775e73@oss.qualcomm.com>

On 4/27/2026 11:04 PM, Shivendra Pratap wrote:
> PSCI has multiple kernel consumers, such as cpuidle-psci-domain.
> Currently, both the PSCI core driver and cpuidle-psci-domain bind
> directly to the same PSCI node "arm,psci-1.0". Additional consumers, if
> introduced, would also need to bind in the same way, leading to several
> drivers attached to a single device node.
> 
> Introduce a PSCI MFD driver that binds to "arm,psci-1.0" and registers
> PSCI child cells. As the first user, register cpuidle-psci-domain as a
> child cell.
> 
> Update cpuidle-psci-domain to probe as an MFD child and use the parent
> PSCI node for power-domain traversal.
> 
> Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>
> ---
>  MAINTAINERS                           |  1 +
>  drivers/cpuidle/Kconfig.arm           |  1 +
>  drivers/cpuidle/cpuidle-psci-domain.c |  9 +-------
>  drivers/mfd/Kconfig                   | 10 ++++++++
>  drivers/mfd/Makefile                  |  2 ++
>  drivers/mfd/psci-mfd.c                | 43 +++++++++++++++++++++++++++++++++++
>  6 files changed, 58 insertions(+), 8 deletions(-)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c871acf2179ce16c80b404027e6b969c7787a6bb..333b01fa00b8fbd15e6f31a6b9af47600411624e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -21239,6 +21239,7 @@ L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/arm/psci.yaml
>  F:	drivers/firmware/psci/
> +F:	drivers/mfd/psci-mfd.c
>  F:	include/linux/psci.h
>  F:	include/uapi/linux/psci.h
>  
> diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
> index a1ee475d180dacab245510674514811aec337ad3..1a1316d11e0a2e4f61801586229f5104e2435865 100644
> --- a/drivers/cpuidle/Kconfig.arm
> +++ b/drivers/cpuidle/Kconfig.arm
> @@ -36,6 +36,7 @@ config ARM_PSCI_CPUIDLE_DOMAIN
>  	bool "PSCI CPU idle Domain"
>  	depends on ARM_PSCI_CPUIDLE
>  	depends on PM_GENERIC_DOMAINS_OF
> +	depends on MFD_PSCI

Should this be select MFD_PSCI?
We're seeing bootup issues on Glymur when MFD_PSCI is not y

>  	select DT_IDLE_GENPD
>  	default y
>  	help
> diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c
> index b9e4ad7d43a3310a76ce62018dff0af1488e33d2..a2d01810f2957abdfa52ae66503dc0cbb205f163 100644
> --- a/drivers/cpuidle/cpuidle-psci-domain.c
> +++ b/drivers/cpuidle/cpuidle-psci-domain.c
> @@ -17,7 +17,6 @@
>  #include <linux/pm_runtime.h>
>  #include <linux/psci.h>
>  #include <linux/slab.h>
> -#include <linux/string.h>
>  
>  #include "cpuidle-psci.h"
>  #include "dt_idle_genpd.h"
> @@ -122,14 +121,9 @@ static void psci_pd_remove(void)
>  	}
>  }
>  
> -static const struct of_device_id psci_of_match[] = {
> -	{ .compatible = "arm,psci-1.0" },
> -	{}
> -};
> -
>  static int psci_cpuidle_domain_probe(struct platform_device *pdev)
>  {
> -	struct device_node *np = pdev->dev.of_node;
> +	struct device_node *np = pdev->dev.parent->of_node;
>  	bool use_osi = psci_has_osi_support();
>  	int ret = 0, pd_count = 0;
>  
> @@ -181,7 +175,6 @@ static struct platform_driver psci_cpuidle_domain_driver = {
>  	.probe  = psci_cpuidle_domain_probe,
>  	.driver = {
>  		.name = "psci-cpuidle-domain",
> -		.of_match_table = psci_of_match,
>  	},
>  };
>  
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 7192c9d1d268e93d1557ca6519ac96056e37e221..75ea5b47315cedfd1153899eef28614a165a14f5 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -2376,6 +2376,16 @@ config MFD_KHADAS_MCU
>  	  additional drivers must be enabled in order to use the functionality
>  	  of the device.
>  
> +config MFD_PSCI
> +	bool "PSCI MFD for psci child cells"
> +	depends on ARM_PSCI_FW
> +	select MFD_CORE
> +	help
> +	  PSCI MFD registers PSCI child cells and exposes them as
> +	  platform devices. Child drivers are probed only if enabled in the
> +	  kernel configuration. Select this option whenever a supported PSCI
> +	  child driver is selected.
> +
>  config MFD_ACER_A500_EC
>  	tristate "Support for Acer Iconia Tab A500 Embedded Controller"
>  	depends on I2C
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index e75e8045c28afae975ac61d282b3b85af5440119..36e872b11b995135a04ca24c33fd3a4d08e4f49a 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -24,6 +24,8 @@ obj-$(CONFIG_MFD_EXYNOS_LPASS)	+= exynos-lpass.o
>  obj-$(CONFIG_MFD_GATEWORKS_GSC)	+= gateworks-gsc.o
>  obj-$(CONFIG_MFD_MACSMC)	+= macsmc.o
>  
> +obj-$(CONFIG_MFD_PSCI)		+= psci-mfd.o
> +
>  obj-$(CONFIG_MFD_TI_LP873X)	+= lp873x.o
>  obj-$(CONFIG_MFD_TI_LP87565)	+= lp87565.o
>  obj-$(CONFIG_MFD_TI_AM335X_TSCADC)	+= ti_am335x_tscadc.o
> diff --git a/drivers/mfd/psci-mfd.c b/drivers/mfd/psci-mfd.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..7affd6bb09dd83452664edeccb09290fe4f43186
> --- /dev/null
> +++ b/drivers/mfd/psci-mfd.c
> @@ -0,0 +1,43 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#include <linux/mfd/core.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +
> +static const struct mfd_cell psci_cells[] = {
> +	{
> +		.name = "psci-cpuidle-domain",
> +	},
> +};
> +
> +static int psci_mfd_probe(struct platform_device *pdev)
> +{
> +	return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, psci_cells,
> +				   ARRAY_SIZE(psci_cells), NULL, 0, NULL);
> +}
> +
> +static const struct of_device_id psci_mfd_of_match[] = {
> +	{ .compatible = "arm,psci-1.0" },
> +	{ }
> +};
> +
> +static struct platform_driver psci_mfd_driver = {
> +	.probe = psci_mfd_probe,
> +	.driver = {
> +		.name = "psci-mfd",
> +		.of_match_table = psci_mfd_of_match,
> +	},
> +};
> +
> +static int __init psci_mfd_init(void)
> +{
> +	return platform_driver_register(&psci_mfd_driver);
> +}
> +
> +core_initcall(psci_mfd_init);
> +
> +MODULE_LICENSE("GPL");
> 



^ permalink raw reply

* Re: [PATCH v4 12/15] arm64: mm: Map the kernel data/bss read-only in the linear map
From: Ard Biesheuvel @ 2026-04-29 14:46 UTC (permalink / raw)
  To: Kevin Brodsky, Ard Biesheuvel, linux-arm-kernel
  Cc: linux-kernel, Will Deacon, Catalin Marinas, Mark Rutland,
	Ryan Roberts, Anshuman Khandual, Liz Prucka, Seth Jenkins,
	Kees Cook, Mike Rapoport, David Hildenbrand, Andrew Morton,
	linux-mm, linux-hardening
In-Reply-To: <a8740f95-e167-48ae-ae65-97c6701d9c94@arm.com>



On Wed, 29 Apr 2026, at 15:54, Kevin Brodsky wrote:
> On 27/04/2026 17:34, Ard Biesheuvel wrote:
>> From: Ard Biesheuvel <ardb@kernel.org>
>>
>> On systems where the bootloader adheres to the original arm64 boot
>> protocol, the placement of the kernel in the physical address space is
>> highly predictable, and this makes the placement of its linear alias in
>> the kernel virtual address space equally predictable, given the lack of
>> randomization of the linear map.
>>
>> The linear aliases of the kernel text and rodata regions are already
>> mapped read-only, but the kernel data and bss are mapped read-write in
>> this region. This is not needed, so map them read-only as well.
>>
>> Note that the statically allocated kernel page tables do need to be
>> modifiable via the linear map, so leave these mapped read-write.
>>
>> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
>> ---
>>  arch/arm64/include/asm/sections.h |  1 +
>>  arch/arm64/mm/mmu.c               | 16 ++++++++++++++--
>>  2 files changed, 15 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/arm64/include/asm/sections.h b/arch/arm64/include/asm/sections.h
>> index 51b0d594239e..32ec21af0823 100644
>> --- a/arch/arm64/include/asm/sections.h
>> +++ b/arch/arm64/include/asm/sections.h
>> @@ -23,6 +23,7 @@ extern char __irqentry_text_start[], __irqentry_text_end[];
>>  extern char __mmuoff_data_start[], __mmuoff_data_end[];
>>  extern char __entry_tramp_text_start[], __entry_tramp_text_end[];
>>  extern char __relocate_new_kernel_start[], __relocate_new_kernel_end[];
>> +extern char __fixmap_pgdir_start[];
>>  
>>  static inline size_t entry_tramp_text_size(void)
>>  {
>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>> index 1a4b4337d29a..9361b7efb848 100644
>> --- a/arch/arm64/mm/mmu.c
>> +++ b/arch/arm64/mm/mmu.c
>> @@ -1122,7 +1122,9 @@ static void __init map_mem(void)
>>  {
>>  	static const u64 direct_map_end = _PAGE_END(VA_BITS_MIN);
>>  	phys_addr_t kernel_start = __pa_symbol(_text);
>> -	phys_addr_t kernel_end = __pa_symbol(__init_begin);
>> +	phys_addr_t init_begin = __pa_symbol(__init_begin);
>> +	phys_addr_t init_end = __pa_symbol(__init_end);
>> +	phys_addr_t kernel_end = __pa_symbol(__fixmap_pgdir_start);
>
> Using fixmap_pgdir as an anchor seems a bit arbitrary... Couldn't we use
> __bss_end instead?
>
> It could also be helpful to add comments in vmlinux.lds.S clarifying
> which sections are RO/RW in the linear map, it's getting pretty
> difficult to follow.
>

Ack.

>>  	phys_addr_t start, end;
>>  	int flags = NO_EXEC_MAPPINGS;
>>  	u64 i;
>> @@ -1155,7 +1157,11 @@ static void __init map_mem(void)
>>  	 * of the region accessible to subsystems such as hibernate,
>>  	 * but protects it from inadvertent modification or execution.
>>  	 */
>> -	__map_memblock(kernel_start, kernel_end, pgprot_tagged(PAGE_KERNEL),
>> +	__map_memblock(kernel_start, init_begin, pgprot_tagged(PAGE_KERNEL),
>> +		       flags);
>> +
>> +	/* Map the kernel data/bss so it can be remapped later */
>> +	__map_memblock(init_end, kernel_end, pgprot_tagged(PAGE_KERNEL),
>
> Maybe I'm missing something obvious, but considering patch 3/4 couldn't
> we directly map the range RO here?
>

After 3/4, __map_memblock() will no longer combine new mappings with existing
ones into block mappings or contiguous ranges. However, it will still set the
requested type and permission attributes on the entire range, and so the second
invocation is needed to restore the read-only bit.

IOW, we could also map it read-only twice, the result would be the same, but the
second call is still needed.



^ permalink raw reply

* Re: [PATCH v6 2/6] media: mediatek: encoder: Add a new encoder driver interface
From: Nicolas Dufresne @ 2026-04-29 14:49 UTC (permalink / raw)
  To: Irui Wang, Hans Verkuil, Mauro Carvalho Chehab, Rob Herring,
	Matthias Brugger, Krzysztof Kozlowski, angelogioacchino.delregno,
	Tiffany Lin, kyrie wu
  Cc: Yunfei Dong, Maoguang Meng, Longfei Wang,
	Project_Global_Chrome_Upstream_Group, linux-media, devicetree,
	linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260423073345.27402-3-irui.wang@mediatek.com>

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

Le jeudi 23 avril 2026 à 15:33 +0800, Irui Wang a écrit :
> Introduce a new encoder kernel driver interface to ensure compatibility
> with the updated encoder software driver running in firmware.
> The new driver interface is expected to support more encoder formats,
> share more encode parameters between kernel and firmware.
> 
> Signed-off-by: Irui Wang <irui.wang@mediatek.com>
> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> ---
>  .../mediatek/vcodec/common/mtk_vcodec_fw.c    |  13 +
>  .../mediatek/vcodec/common/mtk_vcodec_fw.h    |   1 +
>  .../platform/mediatek/vcodec/encoder/Makefile |   1 +
>  .../mediatek/vcodec/encoder/mtk_vcodec_enc.c  |  14 +-
>  .../vcodec/encoder/mtk_vcodec_enc_drv.h       |   8 +-
>  .../vcodec/encoder/venc/venc_common_if.c      | 674 ++++++++++++++++++
>  .../vcodec/encoder/venc/venc_h264_if.c        |   8 +-
>  .../mediatek/vcodec/encoder/venc_drv_if.h     |  11 +-
>  8 files changed, 712 insertions(+), 18 deletions(-)
>  create mode 100644 drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c
> 
> diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
> index 4ed7639dfa30..0381acceda25 100644
> --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
> @@ -18,6 +18,19 @@ int mtk_vcodec_fw_get_ipi(enum mtk_vcodec_fw_type type, int hw_id)
>  }
>  EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_ipi);
>  
> +int mtk_vcodec_fw_get_venc_ipi(enum mtk_vcodec_fw_type type)
> +{
> +	switch (type) {
> +	case SCP:
> +		return SCP_IPI_VENC_H264;
> +	case VCP:
> +		return VCP_IPI_ENCODER;
> +	default:
> +		return -EINVAL;
> +	}

I would prefer to see this placed inside the venc_pdata instead.

Nicolas

> +}
> +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_venc_ipi);
> +
>  struct mtk_vcodec_fw *mtk_vcodec_fw_select(void *priv, enum mtk_vcodec_fw_type type,
>  					   enum mtk_vcodec_fw_use fw_use)
>  {
> diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
> index 142e2e87905c..e7304a7dd3e0 100644
> --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
> @@ -42,5 +42,6 @@ int mtk_vcodec_fw_ipi_send(struct mtk_vcodec_fw *fw, int id,
>  			   void *buf, unsigned int len, unsigned int wait);
>  int mtk_vcodec_fw_get_type(struct mtk_vcodec_fw *fw);
>  int mtk_vcodec_fw_get_ipi(enum mtk_vcodec_fw_type type, int hw_id);
> +int mtk_vcodec_fw_get_venc_ipi(enum mtk_vcodec_fw_type type);
>  
>  #endif /* _MTK_VCODEC_FW_H_ */
> diff --git a/drivers/media/platform/mediatek/vcodec/encoder/Makefile b/drivers/media/platform/mediatek/vcodec/encoder/Makefile
> index e621b5b7e5e6..9d3229d56e39 100644
> --- a/drivers/media/platform/mediatek/vcodec/encoder/Makefile
> +++ b/drivers/media/platform/mediatek/vcodec/encoder/Makefile
> @@ -4,6 +4,7 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-enc.o
>  
>  mtk-vcodec-enc-y := venc/venc_vp8_if.o \
>  		venc/venc_h264_if.o \
> +		venc/venc_common_if.o \
>  		mtk_vcodec_enc.o \
>  		mtk_vcodec_enc_drv.o \
>  		mtk_vcodec_enc_pm.o \
> diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
> index 48cb5dded70a..f0344888f2cf 100644
> --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
> +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
> @@ -81,11 +81,11 @@ static int vidioc_venc_s_ctrl(struct v4l2_ctrl *ctrl)
>  		break;
>  	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
>  		mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_H264_PROFILE val = %d", ctrl->val);
> -		p->h264_profile = ctrl->val;
> +		p->profile = ctrl->val;
>  		break;
>  	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
>  		mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_H264_LEVEL val = %d", ctrl->val);
> -		p->h264_level = ctrl->val;
> +		p->level = ctrl->val;
>  		break;
>  	case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
>  		mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_H264_I_PERIOD val = %d", ctrl->val);
> @@ -385,8 +385,8 @@ static void mtk_venc_set_param(struct mtk_vcodec_enc_ctx *ctx,
>  		mtk_v4l2_venc_err(ctx, "Unsupported fourcc =%d", q_data_src->fmt->fourcc);
>  		break;
>  	}
> -	param->h264_profile = enc_params->h264_profile;
> -	param->h264_level = enc_params->h264_level;
> +	param->profile = enc_params->profile;
> +	param->level = enc_params->level;
>  
>  	/* Config visible resolution */
>  	param->width = q_data_src->visible_width;
> @@ -402,8 +402,8 @@ static void mtk_venc_set_param(struct mtk_vcodec_enc_ctx *ctx,
>  
>  	mtk_v4l2_venc_dbg(0, ctx,
>  			  "fmt 0x%x, P/L %d/%d w/h %d/%d buf %d/%d fps/bps %d/%d gop %d i_per %d",
> -			  param->input_yuv_fmt, param->h264_profile,
> -			  param->h264_level, param->width, param->height,
> +			  param->input_yuv_fmt, param->profile,
> +			  param->level, param->width, param->height,
>  			  param->buf_width, param->buf_height,
>  			  param->frm_rate, param->bitrate,
>  			  param->gop_size, param->intra_period);
> @@ -1157,6 +1157,8 @@ static void mtk_venc_worker(struct work_struct *work)
>  		frm_buf.fb_addr[i].size =
>  				(size_t)src_buf->vb2_buf.planes[i].length;
>  	}
> +	frm_buf.num_planes = src_buf->vb2_buf.num_planes;
> +
>  	bs_buf.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
>  	bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
>  	bs_buf.size = (size_t)dst_buf->vb2_buf.planes[0].length;
> diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
> index 0cddfa13594f..53369adc083b 100644
> --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
> +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
> @@ -69,8 +69,8 @@ enum mtk_encode_param {
>   * @framerate_denom: frame rate denominator. ex: framerate_num=30 and
>   *		     framerate_denom=1 means FPS is 30
>   * @h264_max_qp: Max value for H.264 quantization parameter
> - * @h264_profile: V4L2 defined H.264 profile
> - * @h264_level: V4L2 defined H.264 level
> + * @profile: V4L2 defined profile
> + * @level: V4L2 defined level
>   * @force_intra: force/insert intra frame
>   */
>  struct mtk_enc_params {
> @@ -84,8 +84,8 @@ struct mtk_enc_params {
>  	unsigned int	framerate_num;
>  	unsigned int	framerate_denom;
>  	unsigned int	h264_max_qp;
> -	unsigned int	h264_profile;
> -	unsigned int	h264_level;
> +	unsigned int	profile;
> +	unsigned int	level;
>  	unsigned int	force_intra;
>  };
>  
> diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c
> new file mode 100644
> index 000000000000..050b827f0fd0
> --- /dev/null
> +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c
> @@ -0,0 +1,674 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 MediaTek Inc.
> + */
> +
> +#include "../mtk_vcodec_enc.h"
> +#include "../mtk_vcodec_enc_drv.h"
> +#include "../venc_drv_base.h"
> +#include "../venc_drv_if.h"
> +#include "../venc_vpu_if.h"
> +#include "../../common/mtk_vcodec_intr.h"
> +#include "../../common/mtk_vcodec_util.h"
> +
> +#define SEQ_HEADER_SIZE 1024
> +#define PPS_SIZE 128
> +#define MAX_DPB_SIZE 16
> +#define MAX_VENC_CORE 3
> +#define VENC_CONFIG_LENGTH 115
> +#define VENC_CONFIG_DATA 128
> +#define VENC_PIC_BITSTREAM_BYTE_CNT 0x0098
> +
> +/**
> + * enum venc_bs_mode - encode bitstream mode
> + * @VENC_BS_MODE_SPS: encode sps
> + * @VENC_BS_MODE_PPS: encode pps
> + * @VENC_BS_MODE_VPS: encode vps
> + * @VENC_BS_MODE_SEQ_HDR: encode sequence header
> + * @VENC_BS_MODE_FRAME: encode frame
> + * @VENC_BS_MODE_FRAME_FINAL: encode final frame
> + * @VENC_BS_MODE_MAX: max value
> + */
> +enum venc_bs_mode {
> +	VENC_BS_MODE_SPS = 0,
> +	VENC_BS_MODE_PPS,
> +	VENC_BS_MODE_VPS,
> +	VENC_BS_MODE_SEQ_HDR,
> +	VENC_BS_MODE_FRAME,
> +	VENC_BS_MODE_FRAME_FINAL,
> +	VENC_BS_MODE_MAX
> +};
> +
> +/**
> + * struct venc_config - Structure for encoder configuration
> + *                      AP-W/R : AP is writer/reader on this item
> + *                      MCU-W/R: MCU is write/reader on this item
> + * @input_fourcc: input format fourcc
> + * @bitrate: target bitrate (in bps)
> + * @pic_w: visible width of resolution
> + * @pic_h: visible height of resolution
> + * @buf_w: buffer alignment width of resolution
> + * @buf_h: buffer alignment height of resolution
> + * @gop_size: group of picture size (IDR frame period)
> + * @intra_period: I frame period
> + * @framerate: frame rate in fps
> + * @profile: profile_idc in SPS
> + * @level: level_idc in SPS
> + * @core_num: encoder core num
> + * @dpb_size: encode dpb size
> + * @reserved: reserved fields config
> + */
> +struct venc_config {
> +	__u32 input_fourcc;
> +	__u32 bitrate;
> +	__u32 pic_w;
> +	__u32 pic_h;
> +	__u32 buf_w;
> +	__u32 buf_h;
> +	__u32 gop_size;
> +	__u32 intra_period;
> +	__u32 framerate;
> +	__u32 profile;
> +	__u32 level;
> +	__u32 core_num;
> +	__u32 dpb_size;
> +	__u32 reserved[VENC_CONFIG_LENGTH];
> +};
> +
> +/**
> + * struct venc_config_data - Structure for configuration data
> + * @config_data: extended configuration data besides the basic configuration
> + */
> +struct venc_config_data {
> +	unsigned int config_data[VENC_CONFIG_DATA];
> +};
> +
> +/**
> + * struct venc_work_buf - Structure for working buffer information
> + *                               AP-W/R : AP is writer/reader on this item
> + *                               MCU-W/R: MCU is write/reader on this item
> + * @iova: IO virtual address
> + * @pa: physical address
> + * @pa_64: for 64bit pa padding
> + * @va: virtual address
> + * @va_padding: for 64bit va padding
> + * @size: buffer size
> + * @size_padding: for 64bit size padding
> + */
> +struct venc_work_buf {
> +	unsigned long long iova;
> +	union {
> +		unsigned int pa;
> +		unsigned long long pa_64;
> +	};
> +	union {
> +		void *va;
> +		unsigned long long va_padding;
> +	};
> +	union {
> +		unsigned int size;
> +		unsigned long long size_padding;
> +	};
> +};
> +
> +/**
> + * struct venc_work_buf_list - Structure for encode working buffer list
> + * @rc_code: RC code buffer
> + * @rc_info: RC info buffer
> + * @luma: luma buffer
> + * @chroma: chroma buffer
> + * @sub_luma: sub luma buffer
> + * @sub_write: sub write buffer
> + * @col_mv: col_mv buffer
> + * @wpp: wpp buffer
> + * @wpp_nbm: wpp nbm buffer
> + * @skip_frame: skip frame buffer
> + */
> +struct venc_work_buf_list {
> +	struct venc_work_buf rc_code;
> +	struct venc_work_buf rc_info[MAX_VENC_CORE];
> +	struct venc_work_buf luma[MAX_DPB_SIZE];
> +	struct venc_work_buf chroma[MAX_DPB_SIZE];
> +	struct venc_work_buf sub_luma[MAX_DPB_SIZE];
> +	struct venc_work_buf sub_write[MAX_DPB_SIZE];
> +	struct venc_work_buf col_mv[MAX_DPB_SIZE];
> +	struct venc_work_buf wpp[MAX_VENC_CORE];
> +	struct venc_work_buf wpp_nbm[MAX_VENC_CORE];
> +	struct venc_work_buf skip_frame;
> +};
> +
> +/**
> + * struct venc_info -  Structure for encode frame and bs information
> + * @fb_addr: frame buffer address array
> + * @fb_size: frame buffer size array
> + * @bs_addr: bitstream buffer address
> + * @bs_size: bitstream buffer size
> + */
> +struct venc_info {
> +	unsigned long long fb_addr[VIDEO_MAX_PLANES];
> +	unsigned int fb_size[VIDEO_MAX_PLANES];
> +	unsigned long long bs_addr;
> +	unsigned long long bs_size;
> +};
> +
> +/**
> + * struct venc_vsi - Structure for VCP driver control and info share
> + *                   AP-W/R : AP is writer/reader on this item
> + *                   VCP-W/R: VCP is write/reader on this item
> + * @config: encoder configuration
> + * @data: encoder configuration data
> + * @bufs: encoder working buffers
> + * @venc: encoder information
> + */
> +struct venc_vsi {
> +	struct venc_config config;
> +	struct venc_config_data data;
> +	struct venc_work_buf_list bufs;
> +	struct venc_info venc;
> +};
> +
> +/**
> + * struct venc_inst - Structure for encoder instance
> + * @hw_base: hardware io address
> + * @pps_buf: PPS buffer
> + * @seq_buf: sequence header buffer
> + * @work_buf_allocated: work buffer allocated or not
> + * @frm_cnt: encoded frame count
> + * @skip_frm_cnt: encoded skip frame count
> + * @prepend_hdr: prepend header flag
> + * @vpu_inst: vpu instance
> + * @vsi: encode vsi
> + * @ctx: encoder context
> + */
> +struct venc_inst {
> +	void __iomem *hw_base;
> +	struct mtk_vcodec_mem pps_buf;
> +	struct mtk_vcodec_mem seq_buf;
> +	bool work_buf_allocated;
> +	unsigned int frm_cnt;
> +	unsigned int skip_frm_cnt;
> +	unsigned int prepend_hdr;
> +	struct venc_vpu_inst vpu_inst;
> +	struct venc_vsi *vsi;
> +	struct mtk_vcodec_enc_ctx *ctx;
> +};
> +
> +static int venc_init(struct mtk_vcodec_enc_ctx *ctx)
> +{
> +	int ret = 0;
> +	struct venc_inst *inst;
> +
> +	inst = kzalloc_obj(inst, GFP_KERNEL);
> +	if (!inst)
> +		return -ENOMEM;
> +
> +	inst->ctx = ctx;
> +	inst->vpu_inst.ctx = ctx;
> +	inst->vpu_inst.id = mtk_vcodec_fw_get_venc_ipi(ctx->dev->fw_handler->type);
> +	inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx->dev->reg_base, VENC_SYS);
> +
> +	ret = vpu_enc_init(&inst->vpu_inst);
> +	inst->vsi = (struct venc_vsi *)inst->vpu_inst.vsi;
> +
> +	if (ret) {
> +		kfree(inst);
> +		return ret;
> +	}
> +
> +	ctx->drv_handle = inst;
> +
> +	return 0;
> +}
> +
> +static inline u32 venc_read_reg(struct venc_inst *inst, u32 addr)
> +{
> +	return readl(inst->hw_base + addr);
> +}
> +
> +static unsigned int venc_wait_encode_done(struct venc_inst *inst)
> +{
> +	unsigned int irq_status = 0;
> +	struct mtk_vcodec_enc_ctx *ctx = (struct mtk_vcodec_enc_ctx *)inst->ctx;
> +
> +	if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED,
> +					  WAIT_INTR_TIMEOUT_MS, 0)) {
> +		irq_status = ctx->irq_status;
> +		mtk_venc_debug(ctx, "irq_status %x <-", irq_status);
> +	}
> +	return irq_status;
> +}
> +
> +static void venc_set_bufs(struct venc_inst *inst,
> +			  struct venc_frm_buf *frm_buf,
> +			  struct mtk_vcodec_mem *bs_buf)
> +{
> +	unsigned int i;
> +
> +	if (frm_buf) {
> +		for (i = 0; i < frm_buf->num_planes; i++) {
> +			inst->vsi->venc.fb_addr[i] = frm_buf->fb_addr[i].dma_addr;
> +			inst->vsi->venc.fb_size[i] = frm_buf->fb_addr[i].size;
> +			mtk_venc_debug(inst->ctx, "%s: fb_buf[%d]: %llx(%d)\n",
> +				       __func__, i,
> +				       inst->vsi->venc.fb_addr[i],
> +				       inst->vsi->venc.fb_size[i]);
> +		}
> +	}
> +
> +	if (bs_buf) {
> +		inst->vsi->venc.bs_addr = bs_buf->dma_addr;
> +		inst->vsi->venc.bs_size = bs_buf->size;
> +		mtk_venc_debug(inst->ctx, "%s: bs_buf: %llx(%d)\n",
> +			       __func__,
> +			       inst->vsi->venc.bs_addr,
> +			       (unsigned int)inst->vsi->venc.bs_size);
> +	}
> +}
> +
> +static int venc_encode_sps(struct venc_inst *inst,
> +			   struct mtk_vcodec_mem *bs_buf,
> +			   unsigned int *bs_size)
> +{
> +	int ret = 0;
> +	unsigned int irq_status;
> +
> +	venc_set_bufs(inst, NULL, bs_buf);
> +	ret = vpu_enc_encode(&inst->vpu_inst, VENC_BS_MODE_SPS, NULL, bs_buf, NULL);
> +	if (ret)
> +		return ret;
> +
> +	irq_status = venc_wait_encode_done(inst);
> +	if (irq_status != MTK_VENC_IRQ_STATUS_SPS) {
> +		mtk_venc_err(inst->ctx, "expect irq status %d", MTK_VENC_IRQ_STATUS_SPS);
> +		return -EINVAL;
> +	}
> +
> +	*bs_size = venc_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT);
> +	mtk_venc_debug(inst->ctx, "sps bs size %d <-", *bs_size);
> +
> +	return ret;
> +}
> +
> +static int venc_encode_pps(struct venc_inst *inst,
> +			   struct mtk_vcodec_mem *bs_buf,
> +			   unsigned int *bs_size)
> +{
> +	int ret = 0;
> +	unsigned int irq_status;
> +
> +	venc_set_bufs(inst, NULL, bs_buf);
> +	ret = vpu_enc_encode(&inst->vpu_inst, VENC_BS_MODE_PPS, NULL, bs_buf, NULL);
> +	if (ret)
> +		return ret;
> +
> +	irq_status = venc_wait_encode_done(inst);
> +	if (irq_status != MTK_VENC_IRQ_STATUS_PPS) {
> +		mtk_venc_err(inst->ctx, "expect irq status %d", MTK_VENC_IRQ_STATUS_PPS);
> +		return -EINVAL;
> +	}
> +
> +	*bs_size = venc_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT);
> +	mtk_venc_debug(inst->ctx, "pps bs size %d <-", *bs_size);
> +
> +	return ret;
> +}
> +
> +static int venc_encode_header(struct venc_inst *inst,
> +			      struct mtk_vcodec_mem *bs_buf,
> +			      unsigned int *bs_size)
> +{
> +	int ret = 0;
> +	unsigned int bs_size_sps;
> +	unsigned int bs_size_pps;
> +
> +	ret = venc_encode_sps(inst, bs_buf, &bs_size_sps);
> +	if (ret)
> +		return ret;
> +
> +	ret = venc_encode_pps(inst, &inst->pps_buf, &bs_size_pps);
> +	if (ret)
> +		return ret;
> +
> +	memcpy(bs_buf->va + bs_size_sps, inst->pps_buf.va, bs_size_pps);
> +	*bs_size = bs_size_sps + bs_size_pps;
> +
> +	return ret;
> +}
> +
> +static int venc_encode_frame(struct venc_inst *inst,
> +			     struct venc_frm_buf *frm_buf,
> +			     struct mtk_vcodec_mem *bs_buf,
> +			     unsigned int *bs_size)
> +{
> +	int ret = 0;
> +	unsigned int irq_status;
> +
> +	venc_set_bufs(inst, frm_buf, bs_buf);
> +	ret = vpu_enc_encode(&inst->vpu_inst, VENC_BS_MODE_FRAME, frm_buf, bs_buf, NULL);
> +	if (ret)
> +		return ret;
> +
> +	irq_status = venc_wait_encode_done(inst);
> +	if (irq_status != MTK_VENC_IRQ_STATUS_FRM) {
> +		mtk_venc_err(inst->ctx, "expect irq status %d", MTK_VENC_IRQ_STATUS_FRM);
> +		return -EINVAL;
> +	}
> +
> +	*bs_size = venc_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT);
> +
> +	++inst->frm_cnt;
> +
> +	return ret;
> +}
> +
> +static int venc_encode(void *handle,
> +		       enum venc_start_opt opt,
> +		       struct venc_frm_buf *frm_buf,
> +		       struct mtk_vcodec_mem *bs_buf,
> +		       struct venc_done_result *result)
> +{
> +	int ret = 0;
> +	struct venc_inst *inst = (struct venc_inst *)handle;
> +	struct mtk_vcodec_enc_ctx *ctx;
> +	unsigned int bs_size_hdr;
> +
> +	if (WARN_ON(!inst || !inst->vsi))
> +		return -EINVAL;
> +
> +	ctx = inst->ctx;
> +
> +	mtk_venc_debug(ctx, "%s: opt: %d\n", __func__, opt);
> +
> +	enable_irq(ctx->dev->enc_irq);
> +	switch (opt) {
> +	case VENC_START_OPT_ENCODE_SEQUENCE_HEADER: {
> +		ret = venc_encode_header(inst, bs_buf, &bs_size_hdr);
> +		if (ret)
> +			goto encode_err;
> +
> +		result->bs_size = bs_size_hdr;
> +		result->is_key_frm = false;
> +		break;
> +	}
> +
> +	case VENC_START_OPT_ENCODE_FRAME: {
> +		if (!inst->prepend_hdr) {
> +			ret = venc_encode_frame(inst, frm_buf, bs_buf, &result->bs_size);
> +			if (ret)
> +				goto encode_err;
> +
> +			result->is_key_frm = inst->vpu_inst.is_key_frm;
> +			break;
> +		}
> +
> +		ret = venc_encode_header(inst, &inst->seq_buf, &bs_size_hdr);
> +		if (ret)
> +			goto encode_err;
> +
> +		ret = venc_encode_frame(inst, frm_buf, bs_buf, &result->bs_size);
> +		if (ret)
> +			goto encode_err;
> +
> +		memmove(bs_buf->va + bs_size_hdr, bs_buf->va, result->bs_size);
> +		memcpy(bs_buf->va, inst->seq_buf.va, bs_size_hdr);
> +		result->bs_size += bs_size_hdr;
> +
> +		inst->prepend_hdr = 0;
> +		result->is_key_frm = inst->vpu_inst.is_key_frm;
> +		break;
> +	}
> +
> +	default:
> +		mtk_venc_err(inst->ctx, "venc_opt %d not supported", opt);
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +encode_err:
> +	disable_irq(ctx->dev->enc_irq);
> +	mtk_venc_debug(ctx, "opt %d, return %d", opt, ret);
> +
> +	return ret;
> +}
> +
> +static int mtk_venc_mem_alloc(struct venc_inst *inst,
> +			      struct device *dev,
> +			      struct venc_work_buf *buf)
> +{
> +	dma_addr_t dma_addr;
> +
> +	if (WARN_ON(!dev || !buf))
> +		return -EINVAL;
> +
> +	if (buf->size == 0)
> +		return 0;
> +
> +	buf->va = dma_alloc_coherent(dev, buf->size, &dma_addr, GFP_KERNEL);
> +	if (!buf->va)
> +		return -ENOMEM;
> +
> +	buf->iova = (unsigned long long)dma_addr;
> +
> +	mtk_venc_debug(inst->ctx, "allocate buffer, size: %d, va: %p, iova: 0x%llx",
> +		       buf->size, buf->va, buf->iova);
> +
> +	return 0;
> +}
> +
> +static void mtk_venc_mem_free(struct venc_inst *inst,
> +			      struct device *dev,
> +			      struct venc_work_buf *buf)
> +{
> +	if (WARN_ON(!dev || !buf))
> +		return;
> +
> +	if (!buf->va)
> +		return;
> +
> +	mtk_venc_debug(inst->ctx, "free buffer, size: %d, va: %p, iova: 0x%llx",
> +		       buf->size, buf->va, buf->iova);
> +
> +	dma_free_coherent(dev, buf->size, buf->va, buf->iova);
> +	buf->va = NULL;
> +	buf->iova = 0;
> +	buf->size = 0;
> +}
> +
> +static void venc_free_rc_buf(struct venc_inst *inst,
> +			     struct venc_work_buf_list *bufs,
> +			     unsigned int core_num)
> +{
> +	int i;
> +	struct device *dev;
> +
> +	dev = &inst->ctx->dev->plat_dev->dev;
> +	mtk_venc_mem_free(inst, dev, &bufs->rc_code);
> +
> +	for (i = 0; i < core_num; i++)
> +		mtk_venc_mem_free(inst, dev, &bufs->rc_info[i]);
> +}
> +
> +static void venc_free_work_buf(struct venc_inst *inst)
> +{
> +	int i;
> +	struct venc_work_buf_list *bufs = &inst->vsi->bufs;
> +	unsigned int core_num = inst->vsi->config.core_num;
> +	unsigned int dpb_size = inst->vsi->config.dpb_size;
> +	struct device *dev;
> +
> +	if (bufs->rc_code.va)
> +		venc_free_rc_buf(inst, bufs, core_num);
> +
> +	dev = &inst->ctx->dev->plat_dev->dev;
> +
> +	for (i = 0; i < core_num; i++) {
> +		mtk_venc_mem_free(inst, dev, &bufs->wpp[i]);
> +		mtk_venc_mem_free(inst, dev, &bufs->wpp_nbm[i]);
> +	}
> +
> +	for (i = 0; i < dpb_size; i++) {
> +		mtk_venc_mem_free(inst, dev, &bufs->luma[i]);
> +		mtk_venc_mem_free(inst, dev, &bufs->chroma[i]);
> +		mtk_venc_mem_free(inst, dev, &bufs->sub_luma[i]);
> +		mtk_venc_mem_free(inst, dev, &bufs->sub_write[i]);
> +		mtk_venc_mem_free(inst, dev, &bufs->col_mv[i]);
> +	}
> +
> +	if (inst->pps_buf.va)
> +		mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf);
> +
> +	if (inst->seq_buf.va)
> +		mtk_vcodec_mem_free(inst->ctx, &inst->seq_buf);
> +}
> +
> +static int venc_alloc_rc_buf(struct venc_inst *inst,
> +			     struct venc_work_buf_list *bufs,
> +			     unsigned int core_num)
> +{
> +	int i;
> +	struct mtk_vcodec_fw *fw = inst->ctx->dev->fw_handler;
> +	struct device *dev;
> +	void *tmp_va;
> +
> +	dev = &inst->ctx->dev->plat_dev->dev;
> +	if (mtk_venc_mem_alloc(inst, dev, &bufs->rc_code))
> +		return -ENOMEM;
> +
> +	tmp_va = mtk_vcodec_fw_map_dm_addr(fw, bufs->rc_code.pa);
> +	memcpy(bufs->rc_code.va, tmp_va, bufs->rc_code.size);
> +
> +	for (i = 0; i < core_num; i++) {
> +		if (mtk_venc_mem_alloc(inst, dev, &bufs->rc_info[i]))
> +			goto err_rc_buf;
> +	}
> +
> +	return 0;
> +
> +err_rc_buf:
> +	venc_free_rc_buf(inst, bufs, core_num);
> +
> +	return -ENOMEM;
> +}
> +
> +static int venc_alloc_work_buf(struct venc_inst *inst)
> +{
> +	int i, ret;
> +	struct venc_work_buf_list *bufs = &inst->vsi->bufs;
> +	unsigned int core_num = inst->vsi->config.core_num;
> +	unsigned int dpb_size = inst->vsi->config.dpb_size;
> +	struct device *dev;
> +
> +	if (bufs->rc_code.size != 0) {
> +		ret = venc_alloc_rc_buf(inst, bufs, core_num);
> +		if (ret) {
> +			mtk_venc_err(inst->ctx, "cannot allocate rc buf");
> +			return -ENOMEM;
> +		}
> +	}
> +
> +	dev = &inst->ctx->dev->plat_dev->dev;
> +
> +	for (i = 0; i < core_num; i++) {
> +		if (mtk_venc_mem_alloc(inst, dev, &bufs->wpp[i]) ||
> +		    mtk_venc_mem_alloc(inst, dev, &bufs->wpp_nbm[i]))
> +			goto err_alloc;
> +	}
> +
> +	for (i = 0; i < dpb_size; i++) {
> +		if (mtk_venc_mem_alloc(inst, dev, &bufs->luma[i]) ||
> +		    mtk_venc_mem_alloc(inst, dev, &bufs->chroma[i]) ||
> +		    mtk_venc_mem_alloc(inst, dev, &bufs->sub_luma[i]) ||
> +		    mtk_venc_mem_alloc(inst, dev, &bufs->sub_write[i]) ||
> +		    mtk_venc_mem_alloc(inst, dev, &bufs->col_mv[i]))
> +			goto err_alloc;
> +	}
> +
> +	/* the pps_buf and seq_buf are used by AP side only */
> +	inst->pps_buf.size = PPS_SIZE;
> +	ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->pps_buf);
> +	if (ret) {
> +		mtk_venc_err(inst->ctx, "cannot allocate pps_buf");
> +		goto err_alloc;
> +	}
> +
> +	inst->seq_buf.size = SEQ_HEADER_SIZE;
> +	ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->seq_buf);
> +	if (ret) {
> +		mtk_venc_err(inst->ctx, "cannot allocate seq_buf");
> +		goto err_alloc;
> +	}
> +	return 0;
> +
> +err_alloc:
> +	venc_free_work_buf(inst);
> +	return -ENOMEM;
> +}
> +
> +static int venc_set_param(void *handle,
> +			  enum venc_set_param_type type,
> +			  struct venc_enc_param *enc_prm)
> +{
> +	int ret = 0;
> +	struct venc_inst *inst = (struct venc_inst *)handle;
> +
> +	switch (type) {
> +	case VENC_SET_PARAM_ENC:
> +		if (WARN_ON(!inst->vsi))
> +			return -EINVAL;
> +		inst->vsi->config.input_fourcc = enc_prm->input_yuv_fmt;
> +		inst->vsi->config.bitrate = enc_prm->bitrate;
> +		inst->vsi->config.pic_w = enc_prm->width;
> +		inst->vsi->config.pic_h = enc_prm->height;
> +		inst->vsi->config.buf_w = enc_prm->buf_width;
> +		inst->vsi->config.buf_h = enc_prm->buf_height;
> +		inst->vsi->config.gop_size = enc_prm->gop_size;
> +		inst->vsi->config.framerate = enc_prm->frm_rate;
> +		inst->vsi->config.intra_period = enc_prm->intra_period;
> +		inst->vsi->config.profile = enc_prm->profile;
> +		inst->vsi->config.level = enc_prm->level;
> +
> +		ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm);
> +		if (ret)
> +			break;
> +
> +		if (inst->work_buf_allocated) {
> +			venc_free_work_buf(inst);
> +			inst->work_buf_allocated = false;
> +		}
> +		ret = venc_alloc_work_buf(inst);
> +		if (ret)
> +			break;
> +		inst->work_buf_allocated = true;
> +		break;
> +	case VENC_SET_PARAM_PREPEND_HEADER:
> +		inst->prepend_hdr = 1;
> +		break;
> +	default:
> +		ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm);
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int venc_deinit(void *handle)
> +{
> +	int ret = 0;
> +	struct venc_inst *inst = (struct venc_inst *)handle;
> +
> +	ret = vpu_enc_deinit(&inst->vpu_inst);
> +
> +	if (inst->work_buf_allocated)
> +		venc_free_work_buf(inst);
> +
> +	kfree(inst);
> +
> +	return ret;
> +}
> +
> +const struct venc_common_if venc_if = {
> +	.init = venc_init,
> +	.encode = venc_encode,
> +	.set_param = venc_set_param,
> +	.deinit = venc_deinit,
> +};
> diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
> index d2f4d732d2f7..320c505cdb21 100644
> --- a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
> +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
> @@ -723,9 +723,9 @@ static void h264_enc_set_vsi_configs(struct venc_h264_inst *inst,
>  	inst->vsi->config.framerate = enc_prm->frm_rate;
>  	inst->vsi->config.intra_period = enc_prm->intra_period;
>  	inst->vsi->config.profile =
> -		h264_get_profile(inst, enc_prm->h264_profile);
> +		h264_get_profile(inst, enc_prm->profile);
>  	inst->vsi->config.level =
> -		h264_get_level(inst, enc_prm->h264_level);
> +		h264_get_level(inst, enc_prm->level);
>  	inst->vsi->config.wfd = 0;
>  }
>  
> @@ -742,9 +742,9 @@ static void h264_enc_set_vsi_34_configs(struct venc_h264_inst *inst,
>  	inst->vsi_34->config.framerate = enc_prm->frm_rate;
>  	inst->vsi_34->config.intra_period = enc_prm->intra_period;
>  	inst->vsi_34->config.profile =
> -		h264_get_profile(inst, enc_prm->h264_profile);
> +		h264_get_profile(inst, enc_prm->profile);
>  	inst->vsi_34->config.level =
> -		h264_get_level(inst, enc_prm->h264_level);
> +		h264_get_level(inst, enc_prm->level);
>  	inst->vsi_34->config.wfd = 0;
>  }
>  
> diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h
> index 889440a436b6..3c2a1b5e9312 100644
> --- a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h
> +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h
> @@ -66,8 +66,8 @@ enum venc_set_param_type {
>   * struct venc_enc_prm - encoder settings for VENC_SET_PARAM_ENC used in
>   *					  venc_if_set_param()
>   * @input_fourcc: input yuv format
> - * @h264_profile: V4L2 defined H.264 profile
> - * @h264_level: V4L2 defined H.264 level
> + * @profile: V4L2 defined profile
> + * @level: V4L2 defined level
>   * @width: image width
>   * @height: image height
>   * @buf_width: buffer width
> @@ -79,8 +79,8 @@ enum venc_set_param_type {
>   */
>  struct venc_enc_param {
>  	enum venc_yuv_fmt input_yuv_fmt;
> -	unsigned int h264_profile;
> -	unsigned int h264_level;
> +	unsigned int profile;
> +	unsigned int level;
>  	unsigned int width;
>  	unsigned int height;
>  	unsigned int buf_width;
> @@ -107,9 +107,11 @@ struct venc_frame_info {
>  /*
>   * struct venc_frm_buf - frame buffer information used in venc_if_encode()
>   * @fb_addr: plane frame buffer addresses
> + * @num_planes: number of planes
>   */
>  struct venc_frm_buf {
>  	struct mtk_vcodec_fb fb_addr[MTK_VCODEC_MAX_PLANES];
> +	unsigned int num_planes;
>  };
>  
>  /*
> @@ -124,6 +126,7 @@ struct venc_done_result {
>  
>  extern const struct venc_common_if venc_h264_if;
>  extern const struct venc_common_if venc_vp8_if;
> +extern const struct venc_common_if venc_if;
>  
>  /*
>   * venc_if_init - Create the driver handle

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH v6 4/6] media: mediatek: encoder: Add support for VCP encode process
From: Nicolas Dufresne @ 2026-04-29 14:56 UTC (permalink / raw)
  To: Irui Wang, Hans Verkuil, Mauro Carvalho Chehab, Rob Herring,
	Matthias Brugger, Krzysztof Kozlowski, angelogioacchino.delregno,
	Tiffany Lin, kyrie wu
  Cc: Yunfei Dong, Maoguang Meng, Longfei Wang,
	Project_Global_Chrome_Upstream_Group, linux-media, devicetree,
	linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260423073345.27402-5-irui.wang@mediatek.com>

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

Le jeudi 23 avril 2026 à 15:33 +0800, Irui Wang a écrit :
> Adapt the encoder driver to support VCP firmware interface.
> 
> Set the encoder driver firmware type to 'VCP'.
> Allocate RC buffers using the VCP device.
> Send the shared memory address to VCP and map the encoder VSI address
> to the CPU address space using the VCP shared memory address.
> 
> Signed-off-by: Irui Wang <irui.wang@mediatek.com>
> ---
>  .../mediatek/vcodec/common/mtk_vcodec_fw.c    |  6 +++++
>  .../mediatek/vcodec/common/mtk_vcodec_fw.h    |  1 +
>  .../vcodec/common/mtk_vcodec_fw_priv.h        |  1 +
>  .../vcodec/common/mtk_vcodec_fw_vcp.c         |  6 +++++
>  .../vcodec/encoder/mtk_vcodec_enc_drv.c       |  3 +++
>  .../vcodec/encoder/venc/venc_common_if.c      | 23 ++++++++++++++-----
>  .../mediatek/vcodec/encoder/venc_vpu_if.c     | 14 ++++++++++-
>  7 files changed, 47 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
> index 0381acceda25..7a504f093bd8 100644
> --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
> @@ -105,3 +105,9 @@ int mtk_vcodec_fw_get_type(struct mtk_vcodec_fw *fw)
>  	return fw->type;
>  }
>  EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_type);
> +
> +struct device *mtk_vcodec_fw_get_dev(struct mtk_vcodec_fw *fw)
> +{
> +	return fw->ops->get_fw_dev(fw);
> +}
> +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_dev);
> diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
> index e7304a7dd3e0..56c26b91651e 100644
> --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
> @@ -43,5 +43,6 @@ int mtk_vcodec_fw_ipi_send(struct mtk_vcodec_fw *fw, int id,
>  int mtk_vcodec_fw_get_type(struct mtk_vcodec_fw *fw);
>  int mtk_vcodec_fw_get_ipi(enum mtk_vcodec_fw_type type, int hw_id);
>  int mtk_vcodec_fw_get_venc_ipi(enum mtk_vcodec_fw_type type);
> +struct device *mtk_vcodec_fw_get_dev(struct mtk_vcodec_fw *fw);
>  
>  #endif /* _MTK_VCODEC_FW_H_ */
> diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h
> index 0a2a9b010244..710c83c871f4 100644
> --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h
> @@ -29,6 +29,7 @@ struct mtk_vcodec_fw_ops {
>  	int (*ipi_send)(struct mtk_vcodec_fw *fw, int id, void *buf,
>  			unsigned int len, unsigned int wait);
>  	void (*release)(struct mtk_vcodec_fw *fw);
> +	struct device *(*get_fw_dev)(struct mtk_vcodec_fw *fw);
>  };
>  
>  #if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCODEC_VPU)
> diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
> index 6b69ce44d4bb..2859fe78f67d 100644
> --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
> +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
> @@ -500,6 +500,11 @@ static void mtk_vcodec_vcp_release(struct mtk_vcodec_fw *fw)
>  
>  }
>  
> +static struct device *mtk_vcodec_vcp_get_fw_dev(struct mtk_vcodec_fw *fw)
> +{
> +	return fw->vcp->vcp_device->dev;
> +}
> +
>  static const struct mtk_vcodec_fw_ops mtk_vcodec_vcp_msg = {
>  	.load_firmware = mtk_vcodec_vcp_load_firmware,
>  	.get_vdec_capa = mtk_vcodec_vcp_get_vdec_capa,
> @@ -508,6 +513,7 @@ static const struct mtk_vcodec_fw_ops mtk_vcodec_vcp_msg = {
>  	.ipi_register = mtk_vcodec_vcp_set_ipi_register,
>  	.ipi_send = mtk_vcodec_vcp_ipi_send,
>  	.release = mtk_vcodec_vcp_release,
> +	.get_fw_dev = mtk_vcodec_vcp_get_fw_dev,
>  };
>  
>  struct mtk_vcodec_fw *mtk_vcodec_fw_vcp_init(void *priv, enum mtk_vcodec_fw_use fw_use)
> diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
> index 4e4541b2fc8e..2f6ee0cd15e3 100644
> --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
> +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
> @@ -262,6 +262,9 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
>  	} else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
>  					 &rproc_phandle)) {
>  		fw_type = SCP;
> +	} else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vcp",
> +					 &rproc_phandle)) {
> +		fw_type = VCP;

Similar comment on the patchset you depend on, its third firmware type, and time
to cleanup. The of_device_get_match_data() should be sufficient for this simple
case, there is not reason for custom DT parsing here. PLace the fw_type in the
pdata, and move of_device_get_match_data() call earlier.

Please apply the same logic for the entire patchset. Every bit of information
that is purely static to your device is better placed in the pdata then through
addition of if/else conditions all over the place. The pdata can also have ops
in it, letting easily firmware specific code without having to do if/else
everywhere.

Nicolas

>  	} else {
>  		dev_err(&pdev->dev, "[MTK VCODEC] Could not get venc IPI device");
>  		return -ENODEV;
> diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c
> index 050b827f0fd0..d981155aeb8c 100644
> --- a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c
> +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c
> @@ -480,8 +480,13 @@ static void venc_free_rc_buf(struct venc_inst *inst,
>  {
>  	int i;
>  	struct device *dev;
> +	struct mtk_vcodec_fw *fw = inst->ctx->dev->fw_handler;
> +
> +	if (mtk_vcodec_fw_get_type(fw) == VCP)
> +		dev = mtk_vcodec_fw_get_dev(fw);
> +	else
> +		dev = &inst->ctx->dev->plat_dev->dev;
>  
> -	dev = &inst->ctx->dev->plat_dev->dev;
>  	mtk_venc_mem_free(inst, dev, &bufs->rc_code);
>  
>  	for (i = 0; i < core_num; i++)
> @@ -530,12 +535,18 @@ static int venc_alloc_rc_buf(struct venc_inst *inst,
>  	struct device *dev;
>  	void *tmp_va;
>  
> -	dev = &inst->ctx->dev->plat_dev->dev;
> -	if (mtk_venc_mem_alloc(inst, dev, &bufs->rc_code))
> -		return -ENOMEM;
> +	if (mtk_vcodec_fw_get_type(fw) == VCP) {
> +		dev = mtk_vcodec_fw_get_dev(fw);
> +		if (mtk_venc_mem_alloc(inst, dev, &bufs->rc_code))
> +			return -ENOMEM;
> +	} else {
> +		dev = &inst->ctx->dev->plat_dev->dev;
> +		if (mtk_venc_mem_alloc(inst, dev, &bufs->rc_code))
> +			return -ENOMEM;
>  
> -	tmp_va = mtk_vcodec_fw_map_dm_addr(fw, bufs->rc_code.pa);
> -	memcpy(bufs->rc_code.va, tmp_va, bufs->rc_code.size);
> +		tmp_va = mtk_vcodec_fw_map_dm_addr(fw, bufs->rc_code.pa);
> +		memcpy(bufs->rc_code.va, tmp_va, bufs->rc_code.size);
> +	}
>  
>  	for (i = 0; i < core_num; i++) {
>  		if (mtk_venc_mem_alloc(inst, dev, &bufs->rc_info[i]))
> diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
> index 7772b8442ebc..0f4693e04a9f 100644
> --- a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
> +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
> @@ -8,13 +8,23 @@
>  #include "venc_ipi_msg.h"
>  #include "venc_vpu_if.h"
>  
> +#define VSI_OFFSET_MASK 0x0FFFFFFF
> +
>  static void handle_enc_init_msg(struct venc_vpu_inst *vpu, const void *data)
>  {
>  	const struct venc_vpu_ipi_msg_init_comm *msg = data;
>  	struct mtk_vcodec_fw *fw = vpu->ctx->dev->fw_handler;
> +	u64 pa_start, vsi_offset;
>  
>  	vpu->inst_addr = msg->init_ack.vpu_inst_addr;
> -	vpu->vsi = mtk_vcodec_fw_map_dm_addr(fw, vpu->inst_addr);
> +
> +	if (mtk_vcodec_fw_get_type(fw) == VCP) {
> +		pa_start = (u64)fw->vcp->iova_addr;
> +		vsi_offset = (msg->vpu_vsi_addr & VSI_OFFSET_MASK) - (pa_start & VSI_OFFSET_MASK);
> +		vpu->vsi = mtk_vcodec_fw_map_dm_addr(fw, ENCODER_MEM) + vsi_offset;
> +	} else {
> +		vpu->vsi = mtk_vcodec_fw_map_dm_addr(fw, msg->vpu_vsi_addr);
> +	}
>  
>  	/* Firmware version field value is unspecified on MT8173. */
>  	if (mtk_vcodec_fw_get_type(fw) == VPU)
> @@ -155,6 +165,8 @@ int vpu_enc_init(struct venc_vpu_inst *vpu)
>  	out.base.venc_inst = (unsigned long)vpu;
>  	if (MTK_ENC_DRV_IS_COMM(vpu->ctx)) {
>  		out.codec_type = vpu->ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc;
> +		if (mtk_vcodec_fw_get_type(vpu->ctx->dev->fw_handler) == VCP)
> +			out.shared_iova = vpu->ctx->dev->fw_handler->vcp->iova_addr;
>  		msg_size = sizeof(struct venc_ap_ipi_msg_init_comm);
>  	} else {
>  		msg_size = sizeof(struct venc_ap_ipi_msg_init);

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
From: Padhi, Beleswar @ 2026-04-29 15:04 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Shenwei Wang, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Mathieu Poirier, Frank Li, Sascha Hauer, Shuah Khan,
	linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org, Pengutronix Kernel Team,
	Fabio Estevam, Peng Fan, devicetree@vger.kernel.org,
	linux-remoteproc@vger.kernel.org, imx@lists.linux.dev,
	linux-arm-kernel@lists.infradead.org, dl-linux-imx,
	Bartosz Golaszewski
In-Reply-To: <8d9801cb-0c66-48d8-a946-89a7771e73ea@lunn.ch>


On 4/28/2026 11:35 PM, Andrew Lunn wrote:
>> Remote side learns the endpoint when it receives any message from Linux
>> from the dynamic endpoint.
>>
>> Lets say rpmsg_create_ept() allocates a dynamic local ept of 1026. When
>> you send the message from this endpoint, the standard rpmsg header
>> would have:
>>
>>      85 struct rpmsg_hdr {
>>      86         __rpmsg32 src; // 1026
>>      87         __rpmsg32 dst; // rpdev->dst (e.g. 400)
>>      88         __rpmsg32 reserved;
>>      89         __rpmsg16 len;
>>      90         __rpmsg16 flags;
>>      91         u8 data[];
>>      92 } __packed;
>>
>> Remote side tracks the dynamic endpoint by reading src = 1026. And while
>> sending the response it fills the header as:
> I've never used rpmsg, so this might be a FAQ. How does the remote
> side know what the endpoint is to be used for? Here we are talking
> about GPIO. But the same hardware implements I2C, and a few other
> things. How do we indicate this endpoint is for GPIO?


That's where the channel level isolation helps. GPIO ports are announced
over "rpmsg-io" channel (parent of endpoint), i2c devices would be
announced over "rpmsg-i2c" channel. The default epts in those channels
would have separate addresses (e.g. 0xd for rpmsg-io and 0xe for
rpmsg-i2c). The remote side would have bound a gpio_handling callback
on addr 0xd and a i2c_handling callback on addr 0xe while doing the
channel announcement. So while sending a GPIO msg from Linux, we
should direct it to 0xd dst endpoint, and for sending a i2c message, we
should direct it to 0xe dst endpoint (src ept can be anything). And without
any extra effort, messages sent from dynamic epts at Linux land in the
appropriate handler at remote even for separate device types.

>
> Maybe also related, this hardware also supports a number of GPIO
> controllers. There has been some argument about if one endpoint should
> support multiple GPIO controllers.


IMO no...

>   Or, like gpio-virtio, one endpoint
> represents one GPIO controller, and you instantiate multiple
> endpoints, one per controller.


Yes, this is what I am suggesting in the review of this version.
Basically there should be one rpmsg channel per device type
(e.g. gpio/i2c), and multiple rpmsg endpoints in one channel
representing each instance of the device (e.g. gpio1, gpio2 etc.)

>   How can you tell the different
> instances of GPIO endpoints apart when they are dynamically created?


Well by 2 ways:
1. We can maintain a map of the dynamically created ept and its
     corresponding instance number.This is useful while sending a message
     for a particular instance.
2. rpmsg_create_ept() takes a private data argument, so we just bind the
     per-instance data (struct rpmsg_gpio_port * in this case) to the ept.
     This is useful while receiving a message for a particular instance.

I have suggested an implementation for the same here:
https://lore.kernel.org/all/29485742-6e49-482e-b73d-228295daaeec@ti.com/

Thanks,
Beleswar



^ permalink raw reply

* Re: [PATCH 3/3] interconnect: qcom: Make important drivers default
From: Krzysztof Kozlowski @ 2026-04-29 15:09 UTC (permalink / raw)
  To: Konrad Dybcio, Georgi Djakov, Bjorn Andersson, Konrad Dybcio
  Cc: linux-arm-msm, linux-pm, linux-kernel, linux-arm-kernel
In-Reply-To: <bb02291a-1579-4069-b6b1-757a0b5fb7e6@oss.qualcomm.com>

On 29/04/2026 11:23, Konrad Dybcio wrote:
> On 4/28/26 7:32 PM, Krzysztof Kozlowski wrote:
>> The interconnect drivers for Qualcomm SoC Network-on-Chip are covering a
>> basic or fundamental SoC feature: bandwidth management between internal
>> SoC blocks.  SoC can boot without these, but power management or
>> performance will be affected.  These drivers do not represent any sort
>> of buses visible to the board designers/configurators, thus they should
>> be always enabled, regardless how SoC is used in the final board.
>>
>> Kernel configuration should not ask users choice of drivers when that
>> choice is obvious and known to the developers that answer should be
>> 'yes' or 'module'.
> 
> I'd say let's make them all `default ARCH_QCOM` - all of these drivers
> are required to boot (minus the OSM_L3 driver which is "only" highly
> desired, so that your CPU's bus isn't heavily bottlenecked)

So the few of them should not be a module? That's what you want to say?

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v18 3/8] ring-buffer: Skip invalid sub-buffers when validating persistent ring buffer
From: Masami Hiramatsu @ 2026-04-29 15:15 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Catalin Marinas, Will Deacon, Mathieu Desnoyers, linux-kernel,
	linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <20260428155508.4f47279e@gandalf.local.home>

On Tue, 28 Apr 2026 15:55:08 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Fri, 24 Apr 2026 15:52:27 +0900
> "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> 
> > @@ -5648,11 +5668,12 @@ __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
> >   again:
> >  	/*
> >  	 * This should normally only loop twice. But because the
> > -	 * start of the reader inserts an empty page, it causes
> > -	 * a case where we will loop three times. There should be no
> > -	 * reason to loop four times (that I know of).
> > +	 * start of the reader inserts an empty page, it causes a
> > +	 * case where we will loop three times. There should be no
> > +	 * reason to loop four times unless the ring buffer is a
> > +	 * recovered persistent ring buffer.
> 
> Can you explain more to why this is allowed for persistent ring buffer?

Ah, that was introduced in v15.

  Changes in v15:
  - Skip reader_page loop check on persistent ring buffer because
    there can be contiguous empty(invalidated) pages. 


So, finding next reader_page, we need to skip empty pages, which is normally
not contiguous. However, if we see more than 3 invalid pages on recovering
persistent ring buffer, it will be reset and become empty.


> 
> Note, I do not like any loops that can go into an infinite loop and lock up
> the machine. If something goes wrong with a persistent ring buffer, then
> this could possibly go into an infinite loop.

Yeah, so I think we should not use goto here. OK, let me update it to
an actual loop.

> 
> I want to understand why this is allowed, and possibly add a check that
> prevents this from never ending.

It should not be a never ending loop (there are other exit conditions),
but I agreed. What about limiting with nr_subbufs?

diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 5326924615a4..aa89ec96e964 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -5630,7 +5630,8 @@ __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
 	 * a case where we will loop three times. There should be no
 	 * reason to loop four times (that I know of).
 	 */
-	if (RB_WARN_ON(cpu_buffer, ++nr_loops > 3)) {
+	if (RB_WARN_ON(cpu_buffer,
+		++nr_loops > (cpu_buffer->ring_meta ? cpu_buffer->nr_subbufs : 3))) {
 		reader = NULL;
 		goto out;
 	}

> 
> -- Steve
> 
> 
> >  	 */
> > -	if (RB_WARN_ON(cpu_buffer, ++nr_loops > 3)) {
> > +	if (RB_WARN_ON(cpu_buffer, ++nr_loops > 3 && !cpu_buffer->ring_meta)) {
> >  		reader = NULL;
> >  		goto out;
> >  	}
> 


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>


^ permalink raw reply related

* Re: [PATCH v18 4/8] ring-buffer: Skip invalid sub-buffers when rewinding persistent ring buffer
From: Masami Hiramatsu @ 2026-04-29 15:20 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Catalin Marinas, Will Deacon, Mathieu Desnoyers, linux-kernel,
	linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <20260428162146.78e52988@gandalf.local.home>

On Tue, 28 Apr 2026 16:21:46 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Fri, 24 Apr 2026 15:52:35 +0900
> "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> 
> > @@ -1892,9 +1895,19 @@ static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu,
> >  	 * subbuf_size is considered invalid.
> >  	 */
> >  	tail = local_read(&dpage->commit) & ~RB_MISSED_MASK;
> > -	if (tail > meta->subbuf_size - BUF_PAGE_HDR_SIZE)
> > -		return -1;
> > -	return rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
> > +	if (tail <= meta->subbuf_size - BUF_PAGE_HDR_SIZE)
> > +		ret = rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
> > +
> 
> This code seriously needs comments.

OK, I'll add it, or let code explain clearer?

	if (tail <= meta->subbuf_size - BUF_PAGE_HDR_SIZE)
		ret = rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
	else
		ret = -1;

Thanks,

> 
> -- Steve
> 
> > +	if (ret < 0 || (prev_ts && prev_ts > ts) || (next_ts && ts > next_ts)) {
> > +		local_set(&bpage->entries, 0);
> > +		local_set(&bpage->page->commit, 0);
> > +		bpage->page->time_stamp = prev_ts ? prev_ts : next_ts;
> > +		ret = -1;
> > +	} else {
> > +		local_set(&bpage->entries, ret);
> > +	}
> > +
> > +	return ret;
> >  }
> >  


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>


^ permalink raw reply

* Re: [PATCH v4 00/11] thermal: samsung: Add support for Google GS101 TMU
From: Tudor Ambarus @ 2026-04-29 15:20 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Rafael J. Wysocki, Zhang Rui, Lukasz Luba,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Alim Akhtar,
	Bartlomiej Zolnierkiewicz, Kees Cook, Gustavo A. R. Silva,
	Peter Griffin, André Draszik, Daniel Lezcano,
	Sylwester Nawrocki, Chanwoo Choi, Michael Turquette, Stephen Boyd,
	Lee Jones
  Cc: willmcvicker, jyescas, shin.son, linux-samsung-soc, linux-kernel,
	linux-pm, devicetree, linux-arm-kernel, linux-hardening,
	linux-clk, Krzysztof Kozlowski
In-Reply-To: <41ebf31c-89ca-497e-b206-ece332d0260e@kernel.org>

Hi, Krzysztof,

On 4/28/26 12:24 PM, Krzysztof Kozlowski wrote:
>> Dependencies
>> ============
>> - context dependency on the ACPM fixes sent at:
>>   Link: https://lore.kernel.org/linux-samsung-soc/20260423-acpm-fixes-sashiko-reports-v1-0-2217b790925e@linaro.org/T/#t
>> - cleanup and prep firmware patches 2, 3, 4, 5, 6, 7: required by the
>>   thermal driver (patch 8).
> Well, cleanup in one driver cannot be a dependency for other driver.
> 
>> - bindings (patch 1): required for DTS validation.
>> - thermal driver patch 8: required by defconfig (patch 11) - logical
>> dependency. 
> Most of the patches should be taken before, which would leave you only
> final new firmware interface as dependency. Creating such unnecessary
> dependencies is not helping.
> 
> And the patchset even grew and will conflict with other firmware work,
> so I will deal with firmware bits and I can provide them via stable tag
> to thermal for the last patch.

Sounds good, thank you!

I'll send the firmware cleanup and preparatory patches in their own patch
set after the firmware fixes get in.

Then I will resend the thermal driver once all the firmware patches are
sorted out and a stable tag is provided.

In the meantime the thermal maintainers can still check the thermal driver
proposal.

Cheers,
ta


^ permalink raw reply

* Re: [PATCH v18 7/8] ring-buffer: Cleanup persistent ring buffer validation
From: Masami Hiramatsu @ 2026-04-29 15:21 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Catalin Marinas, Will Deacon, Mathieu Desnoyers, linux-kernel,
	linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <20260428162457.1ca8c4b6@gandalf.local.home>

On Tue, 28 Apr 2026 16:24:57 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Fri, 24 Apr 2026 15:52:59 +0900
> "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> 
> > From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > 
> > Cleanup rb_meta_validate_events() function to make it easier to read.
> > This includes the following cleanups:
> >  - Introduce rb_validatation_state to hold working variables in
> >    validation.
> >  - Move repleated validation state updates into rb_validate_buffer().
> >  - Move reader_page injection code outside of rb_meta_validate_events().
> > 
> > Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
> > ---
> >  kernel/trace/ring_buffer.c |  186 ++++++++++++++++++++++----------------------
> >  1 file changed, 95 insertions(+), 91 deletions(-)
> > 
> > diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> > index de653a8e3cec..9850a0d8d24b 100644
> > --- a/kernel/trace/ring_buffer.c
> > +++ b/kernel/trace/ring_buffer.c
> > @@ -1883,8 +1883,16 @@ static int rb_read_data_buffer(struct buffer_data_page *dpage, int tail, int cpu
> >  	return events;
> >  }
> >  
> > -static int rb_validate_buffer(struct buffer_page *bpage, int cpu,
> > -			      struct ring_buffer_cpu_meta *meta, u64 prev_ts, u64 next_ts)
> > +struct rb_validation_state {
> > +	unsigned long entries;
> > +	unsigned long entry_bytes;
> > +	int discarded;
> > +	u64 ts;
> > +};
> > +
> > +static int __rb_validate_buffer(struct buffer_page *bpage, int cpu,
> > +				struct ring_buffer_cpu_meta *meta,
> > +				u64 prev_ts, u64 next_ts)
> >  {
> 
> This can still use those comments (from patch 4).
> 
> Also, could you rebase on top of v7.1-rc1?

Is it v7.1-rc1, not ring-buffer/for-next?

Thanks,

> 
> Thanks Masami!
> 
> -- Steve
> 
> >  	struct buffer_data_page *dpage = bpage->page;
> >  	unsigned long long ts;
> > @@ -1914,16 +1922,82 @@ static int rb_validate_buffer(struct buffer_page *bpage, int cpu,
> >  	return ret;
> >  }


-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>


^ permalink raw reply

* Re: [PATCH v5 3/8] arm64: entry: add unwind info for various kernel entries
From: Mark Rutland @ 2026-04-29 15:26 UTC (permalink / raw)
  To: Dylan Hatch
  Cc: Roman Gushchin, Weinan Liu, Will Deacon, Josh Poimboeuf,
	Indu Bhagat, Peter Zijlstra, Steven Rostedt, Catalin Marinas,
	Jiri Kosina, Jens Remus, Prasanna Kumar T S M, Puranjay Mohan,
	Song Liu, joe.lawrence, linux-toolchains, linux-kernel,
	live-patching, linux-arm-kernel, Randy Dunlap
In-Reply-To: <20260428183643.3796063-4-dylanbhatch@google.com>

Hi Dylan,

On Tue, Apr 28, 2026 at 06:36:38PM +0000, Dylan Hatch wrote:
> From: Weinan Liu <wnliu@google.com>
> 
> DWARF CFI (Call Frame Information) specifies how to recover the return
> address and callee-saved registers at each PC in a given function.
> Compilers are able to generate the CFI annotations when they compile
> the code to assembly language. For handcrafted assembly, we need to
> annotate them by hand.
> 
> Annotate minimal CFI to enable stacktracing using SFrame for kernel
> exception entries through el1*_64_*() paths 

I thought we were only consuming SFrame when unwinding an exeption
boundary?

We shouldn't be taking exceptions _from_ the entry assembly functions
unless something has gone horribly wrong, and so I don't see why we'd
need CFI entries for the entry assembly functions.

Am I missing some reason we need CFI entries for the entry assembly
functions? I strongly suspect it is not necessary to add these, and I'd
prefer to omit them.

> and irq entries through call_on_irq_stack()

Needing some sort of unwind annotations for call_on_irq_stack() makes
sense to me, but don't we need something for other assembly functions
too?

We can interrupt things like memset(); I assume we'll treat those as
unreliable until annotated?

Mark.

> Signed-off-by: Weinan Liu <wnliu@google.com>
> Suggested-by: Jens Remus <jremus@linux.ibm.com>
> Reviewed-by: Jens Remus <jremus@linux.ibm.com>
> Signed-off-by: Dylan Hatch <dylanbhatch@google.com>
> ---
>  arch/arm64/kernel/entry.S | 23 +++++++++++++++++++++++
>  1 file changed, 23 insertions(+)
> 
> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
> index f8018b5c1f9a..dc55b0b19cfa 100644
> --- a/arch/arm64/kernel/entry.S
> +++ b/arch/arm64/kernel/entry.S
> @@ -30,6 +30,12 @@
>  #include <asm/asm-uaccess.h>
>  #include <asm/unistd.h>
>  
> +/*
> + * Do not generate .eh_frame.  Only generate .debug_frame and optionally
> + * .sframe (via assembler option --gsframe[-N]).
> + */
> +	.cfi_sections .debug_frame
> +
>  	.macro	clear_gp_regs
>  	.irp	n,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29
>  	mov	x\n, xzr
> @@ -575,7 +581,16 @@ SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label)
>  	.if \el == 0
>  	b	ret_to_user
>  	.else
> +	/*
> +	 * Minimal DWARF CFI for unwinding across the call above.
> +	 * Enable unwinding for el1*_64_*() path only.
> +	 */
> +	.cfi_startproc
> +	.cfi_def_cfa_offset PT_REGS_SIZE
> +	.cfi_offset 29, S_FP - PT_REGS_SIZE
> +	.cfi_offset 30, S_LR - PT_REGS_SIZE
>  	b	ret_to_kernel
> +	.cfi_endproc
>  	.endif
>  SYM_CODE_END(el\el\ht\()_\regsize\()_\label)
>  	.endm
> @@ -872,6 +887,7 @@ NOKPROBE(ret_from_fork)
>   * Calls func(regs) using this CPU's irq stack and shadow irq stack.
>   */
>  SYM_FUNC_START(call_on_irq_stack)
> +	.cfi_startproc
>  	save_and_disable_daif x9
>  #ifdef CONFIG_SHADOW_CALL_STACK
>  	get_current_task x16
> @@ -882,6 +898,9 @@ SYM_FUNC_START(call_on_irq_stack)
>  	/* Create a frame record to save our LR and SP (implicit in FP) */
>  	stp	x29, x30, [sp, #-16]!
>  	mov	x29, sp
> +	.cfi_def_cfa 29, 16
> +	.cfi_offset 29, -16
> +	.cfi_offset 30, -8
>  
>  	ldr_this_cpu x16, irq_stack_ptr, x17
>  
> @@ -897,9 +916,13 @@ SYM_FUNC_START(call_on_irq_stack)
>  	 */
>  	mov	sp, x29
>  	ldp	x29, x30, [sp], #16
> +	.cfi_restore 29
> +	.cfi_restore 30
> +	.cfi_def_cfa 31, 0
>  	scs_load_current
>  	restore_irq x9
>  	ret
> +	.cfi_endproc
>  SYM_FUNC_END(call_on_irq_stack)
>  NOKPROBE(call_on_irq_stack)
>  
> -- 
> 2.54.0.545.g6539524ca2-goog
> 


^ permalink raw reply

* Re: [PATCH 1/4] soc: qcom: Hide all drivers behind selectable menu
From: Krzysztof Kozlowski @ 2026-04-29 15:30 UTC (permalink / raw)
  To: Konrad Dybcio, Bjorn Andersson, Konrad Dybcio
  Cc: linux-arm-msm, linux-kernel, linux-arm-kernel
In-Reply-To: <82602963-6caa-428a-9b39-e0cfd42ae87f@oss.qualcomm.com>

On 29/04/2026 11:28, Konrad Dybcio wrote:
>> +config QCOM_PDR_HELPERS
>> +	tristate
>> +	select QCOM_QMI_HELPERS
>> +	select QCOM_PDR_MSG
>> +	depends on NET
>> +
>> +config QCOM_QMI_HELPERS
>> +	tristate
>> +	depends on NET
> 
> also:
> 
> QCOM_KRYO_L2_ACCESSORS -> drivers/perf/Kconfig

Ack

> QCOM_SPM -> drivers/cpuidle/Kconfig.arm

Heh, that's user-selectable driver, so I think we should change the
select into dependency. It will make choice a bit more complex for arm32
- the SPM cpuidle driver will be hidden if this is not selected. But
alternative is that this driver will be outside of entire menuconfig.

Does changing to "depends" feel right?

> 
> Konrad


Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v3 00/15] SCMI Clock rates discovery rework
From: Florian Fainelli @ 2026-04-29 15:39 UTC (permalink / raw)
  To: Cristian Marussi, linux-kernel, linux-arm-kernel, arm-scmi,
	linux-clk, linux-renesas-soc
  Cc: sudeep.holla, philip.radford, james.quinlan, vincent.guittot,
	etienne.carriere, peng.fan, michal.simek, geert+renesas,
	kuninori.morimoto.gx, marek.vasut+renesas
In-Reply-To: <20260428201522.903875-1-cristian.marussi@arm.com>



On 4/28/2026 1:15 PM, Cristian Marussi wrote:
> Hi,
> 
> it was a known limitation, in the SCMI Clock protocol support, the lack of
> dynamic allocation around per-clock rates discovery: fixed size statically
> per-clock rates arrays did not scale and was increasingly a waste of memory
> (see [1]).
> 
> This series aim at solving this in successive steps:
> 
>   - simplify and reduce to the minimum possible the rates data info exposed
>     to the SCMI driver by scmi_clock_info
>   - move away from static fixed allocation of per-clock rates arrays in
>     favour of a completely dynamic runtime allocation: just allocate what
>     is needed based on the effectively discovered
> 
> This is done in patches 2-6.
> 
> A further bigger optimization suggested in a past series [2] by Etienne
> would be, whenever allowed by the spec, to limit upfront the number of
> queries in order to simply retrieve min and max rate, that are indeed the
> only rates needed by the CLK SCMI driver.
> 
> The approach proposed in [1] was open coding and duplicating some of the
> functionalities already provided by SCMI iterators, though.
> 
> Patch 7-14 implement such optimization instead by:
> 
>   - reworking core SCMI iterators to support bound enumerations
>   - use such new bound iterators to perform the minimum number of queries
>     in order to only retrieve min an max rate
> 
> As a final result now the rates enumeration triggered by the CLK SCMI
> driver, while still allocating for all the existent rates, miminize the
> number of SCMI CLK_DESCRIBE_RATE messages needed to obtain min and max.
> 
> Finally, patch 15 introduces a new clock protocol operation to be able to
> trigger anytime on demand a full enumeration and obtain the full list of
> rates when needed, not only min/max: this latter method is really only used
> currently by some dowstream SCMI Test driver of mine.
> 
> Most notably in this V3 I had:
> 
>   - picked up Geert fixes on V2: these could have been squashed in the
>     original series while maintaining proper Geert's authorship but as of
>     now I have simply picked them up and changed their order to be near the
>     commit they fix
> 
>   - dropped the "Harden Clock protocol initialization" patch that caused a
>     number of out-of-spec vendor FW to break
> 
> Based on v7.1-rc1.
> Tested on ARM/JUNO, RADXA/ROCK5B and an emulated environment.

Tested-by: Florian Fainelli <florian.fainelli@broadcom.com>
-- 
Florian


^ permalink raw reply

* Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
From: Mathieu Poirier @ 2026-04-29 15:41 UTC (permalink / raw)
  To: Shenwei Wang
  Cc: Andrew Lunn, Padhi, Beleswar, Linus Walleij, Bartosz Golaszewski,
	Jonathan Corbet, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Frank Li, Sascha Hauer, Shuah Khan,
	linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org, Pengutronix Kernel Team,
	Fabio Estevam, Peng Fan, devicetree@vger.kernel.org,
	linux-remoteproc@vger.kernel.org, imx@lists.linux.dev,
	linux-arm-kernel@lists.infradead.org, dl-linux-imx,
	Bartosz Golaszewski
In-Reply-To: <PAXPR04MB9185A098D894B6A6EBCC13F889372@PAXPR04MB9185.eurprd04.prod.outlook.com>

On Tue, Apr 28, 2026 at 03:24:59PM +0000, Shenwei Wang wrote:
> 
> 
> > -----Original Message-----
> > From: Andrew Lunn <andrew@lunn.ch>
> > Sent: Monday, April 27, 2026 3:49 PM
> > To: Shenwei Wang <shenwei.wang@nxp.com>
> > Cc: Padhi, Beleswar <b-padhi@ti.com>; Linus Walleij <linusw@kernel.org>;
> > Bartosz Golaszewski <brgl@kernel.org>; Jonathan Corbet <corbet@lwn.net>;
> > Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>;
> > Conor Dooley <conor+dt@kernel.org>; Bjorn Andersson
> > <andersson@kernel.org>; Mathieu Poirier <mathieu.poirier@linaro.org>; Frank Li
> > <frank.li@nxp.com>; Sascha Hauer <s.hauer@pengutronix.de>; Shuah Khan
> > <skhan@linuxfoundation.org>; linux-gpio@vger.kernel.org; linux-
> > doc@vger.kernel.org; linux-kernel@vger.kernel.org; Pengutronix Kernel Team
> > <kernel@pengutronix.de>; Fabio Estevam <festevam@gmail.com>; Peng Fan
> > <peng.fan@nxp.com>; devicetree@vger.kernel.org; linux-
> > remoteproc@vger.kernel.org; imx@lists.linux.dev; linux-arm-
> > kernel@lists.infradead.org; dl-linux-imx <linux-imx@nxp.com>; Bartosz
> > Golaszewski <brgl@bgdev.pl>
> > Subject: [EXT] Re: [PATCH v13 3/4] gpio: rpmsg: add generic rpmsg GPIO driver
> > > > struct virtio_gpio_response {
> > > >         __u8 status;
> > > >         __u8 value;
> > > > };
> > 
> > > It is the same message format. Please see the message definition
> > (GET_DIRECTION) below:
> > 
> > > +   +-----+-----+-----+-----+-----+----+
> > > +   |0x00 |0x01 |0x02 |0x03 |0x04 |0x05|
> > > +   | 1   | 2   |port |line | err | dir|
> > > +   +-----+-----+-----+-----+-----+----+
> > 
> > Sorry, but i don't see how two u8 vs six u8 are the same message format.
> > 
> 
> Some changes to the message format are necessary.
> 
> Virtio uses two communication channels (virtqueues): one for requests and replies, and a second one for events. 
> In contrast, rpmsg provides only a single communication channel, so a type field is required to distinguish between 
> different kinds of messages.
> 
> Since rpmsg replies and events share the same message format, an additional line is introduced to handle both cases.
> 
> Finally, rpmsg supports multiple GPIO controllers, so a port field is added to uniquely identify the target controller.

I have commented on this before - RPMSG is already providing multiplexing
capability by way of endpoints.  There is no need for a port field.  One
endpoint, one GPIO controller. 

> 
> Shenwei
> 
> >        Andrew


^ permalink raw reply

* Re: [PATCH v18 7/8] ring-buffer: Cleanup persistent ring buffer validation
From: Steven Rostedt @ 2026-04-29 15:50 UTC (permalink / raw)
  To: Masami Hiramatsu (Google)
  Cc: Catalin Marinas, Will Deacon, Mathieu Desnoyers, linux-kernel,
	linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <20260430002139.c3b0e4c92ae52aeeaf86e1bf@kernel.org>

On Thu, 30 Apr 2026 00:21:39 +0900
Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote:

> > Also, could you rebase on top of v7.1-rc1?  
> 
> Is it v7.1-rc1, not ring-buffer/for-next?

The ring-buffer/for-next has been merged with conflicts. I rather not add
more conflicts ;-)  v7.1-rc1 includes ring-buffer/for-next.

-- Steve


^ permalink raw reply

* Re: [PATCH 09/43] KVM: arm64: gic-v5: Implement VMT/vIST IRS MMIO Ops
From: Marc Zyngier @ 2026-04-29 16:04 UTC (permalink / raw)
  To: Sascha Bischoff
  Cc: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
	kvm@vger.kernel.org, nd, oliver.upton@linux.dev, Joey Gouly,
	Suzuki Poulose, yuzenghui@huawei.com, peter.maydell@linaro.org,
	lpieralisi@kernel.org, Timothy Hayes
In-Reply-To: <20260427160547.3129448-10-sascha.bischoff@arm.com>

On Mon, 27 Apr 2026 17:09:06 +0100,
Sascha Bischoff <Sascha.Bischoff@arm.com> wrote:
> 
> GICv5 has rules about which fields of a VMTE (or L1 VMT) may be
> directly written by the host once the table is valid. This ensures
> that no stale state is cached by the hardware, and provides a clear
> interface for making VMs, ISTs, etc, valid.
> 
> The hypervisor is responsible for populating the VMTE for a
> VM. However, it is not permitted to write the Valid bit (as the VM
> table is already valid). Instead, the VM is made valid via an IRS MMIO
> Op. The same applies to the ISTs - they must be made valid via the
> host IRS.
> 
> This commit adds support for:
> 
> * Making level 2 VMTs valid (only), allowing for dynamic level 2 table
>   allocation.

Isn't it level 1 instead, if L2 is supposed to be dynamic?

> * Making VMTEs (VMs) valid or invalid
> * Making SPI/LPI ISTs valid or invalid for a specific VM
> 
> When (successfully) probing for a GICv5, the VMT is allocated, and is
> made valid via the IRS's MMIO interface.
> 
> This commit also extends the doorbell domain to allow the doorbells
> themselves to act as a conduit for issuing commands - this is similar
> to what exists for GICv4 support. Effectively, irq_set_vcpu_affinity()
> becomes an ioctl-like interface for issuing commands specific to
> either a VM or the particular VPE that the doorbell belongs to. This
> change adds support for the following via the VPE doorbells:
> 
>         VMT_L2_MAP - Make a second level VM table valid
>         VMTE_MAKE_VALID - Make a single VMTE (and hence VM) valid
>         VMTE_MAKE_INVALID - Make a single VMTE (and hence VM) invalid
>         SPI_VIST_MAKE_VALID - Make the SPI IST valid
>         LPI_VIST_MAKE_VALID - Make the LPI IST valid
>         LPI_VIST_MAKE_INVALID - Make the LPI IST invalid
> 
> Note: It is intentional that there is no SPI_VIST_MAKE_INVALID - this
> cannot happen while the VM is live, and given that the SPI is

This SPI_VIST_MAKE_VALID introduced in the previous patch. It feels
weird to only explain the lack of INVALID here...

> allocated as part of VM creation, there is no need to make it invalid
> again until the VM is destroyed, at which point the VMTE is
> invalid. Therefore, there's no need to do this via the host's IRS MMIO
> interface, as it can be directly marked as invalid and freed. LPIs, on
> the other hand, are driven by the guest itself, and the guest is
> theoretically free to invalidate and free the LPI IST at any point.
> 
> Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
> ---
>  arch/arm64/kvm/vgic/vgic-v5-tables.c |  25 +++
>  arch/arm64/kvm/vgic/vgic-v5-tables.h |   2 +
>  arch/arm64/kvm/vgic/vgic-v5.c        | 236 ++++++++++++++++++++++++++-
>  include/linux/irqchip/arm-gic-v5.h   |  30 ++++
>  4 files changed, 290 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm64/kvm/vgic/vgic-v5-tables.c b/arch/arm64/kvm/vgic/vgic-v5-tables.c
> index de905f37b61a5..0120c3205dea6 100644
> --- a/arch/arm64/kvm/vgic/vgic-v5-tables.c
> +++ b/arch/arm64/kvm/vgic/vgic-v5-tables.c
> @@ -666,6 +666,26 @@ int vgic_v5_vmte_free_vpe(struct kvm_vcpu *vcpu)
>  	return 0;
>  }
>  
> +phys_addr_t vgic_v5_get_vmt_base(void)
> +{
> +	phys_addr_t vmt_base;
> +
> +	if (!vgic_v5_vmt_allocated())
> +		return -ENXIO;
> +
> +	if (!vmt_info->two_level)
> +		vmt_base = virt_to_phys(vmt_info->linear.vmt_base);
> +	else
> +		vmt_base = virt_to_phys(vmt_info->l2.vmt_base);
> +
> +	return vmt_base;
> +}
> +
> +u8 vgic_v5_vmt_vpe_id_bits(void)
> +{
> +	return fls(vmt_info->max_vpes) - 1;
> +}
> +
>  /*
>   * Assign an already allocated IST to the VM by populating the fields in the
>   * corresponding VMTE. We re-use this code for both an SPI IST and LPI IST, even
> @@ -715,6 +735,11 @@ int vgic_v5_vmte_assign_ist(struct kvm *kvm, phys_addr_t ist_base,
>  	/* Finally, mark the entry as valid */
>  	cmd_info.cmd_type = spi_ist ? SPI_VIST_MAKE_VALID : LPI_VIST_MAKE_VALID;
>  	ret = irq_set_vcpu_affinity(vgic_v5_vpe_db(vcpu0), &cmd_info);
> +	if (ret) {
> +		WRITE_ONCE(vmte->val[section], 0ULL);
> +		vgic_v5_clean_inval(vmte, sizeof(*vmte), true, false);
> +		return ret;
> +	}
>  
>  	/* Any cached entries we now have are stale! */
>  	vgic_v5_clean_inval(vmte, sizeof(*vmte), false, true);
> diff --git a/arch/arm64/kvm/vgic/vgic-v5-tables.h b/arch/arm64/kvm/vgic/vgic-v5-tables.h
> index 37e220cda1987..6a024337eba79 100644
> --- a/arch/arm64/kvm/vgic/vgic-v5-tables.h
> +++ b/arch/arm64/kvm/vgic/vgic-v5-tables.h
> @@ -150,6 +150,8 @@ int vgic_v5_vmt_allocate(bool two_level, unsigned int num_entries,
>  			 size_t vmd_size, size_t vped_size,
>  			 unsigned int vpe_id_bits);
>  int vgic_v5_vmt_free(void);
> +phys_addr_t vgic_v5_get_vmt_base(void);
> +u8 vgic_v5_vmt_vpe_id_bits(void);
>  
>  int vgic_v5_allocate_vm_id(struct kvm *kvm);
>  void vgic_v5_release_vm_id(struct kvm *kvm);
> diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c
> index 4e0d52b309628..49eb01ca07961 100644
> --- a/arch/arm64/kvm/vgic/vgic-v5.c
> +++ b/arch/arm64/kvm/vgic/vgic-v5.c
> @@ -36,6 +36,12 @@ static void vgic_v5_get_implemented_ppis(void)
>  	__assign_bit(GICV5_ARCH_PPI_PMUIRQ, ppi_caps.impl_ppi_mask, system_supports_pmuv3());
>  }
>  
> +/*
> + * The IRS MMIO interface is shared between all VMs, so make sure we don't do
> + * anything stupid!
> + */
> +static DEFINE_RAW_SPINLOCK(vm_config_lock);
> +

I don't think you could have picked a worse name for this lock. It has
nothing to do with a VM. It really is a global IRS lock.

>  static void __iomem *irs_base;
>  
>  static u32 irs_readl_relaxed(const u32 reg_offset)
> @@ -43,6 +49,21 @@ static u32 irs_readl_relaxed(const u32 reg_offset)
>  	return readl_relaxed(irs_base + reg_offset);
>  }
>  
> +static void irs_writel_relaxed(const u32 val, const u32 reg_offset)
> +{
> +	writel_relaxed(val, irs_base + reg_offset);
> +}
> +
> +static u64 irs_readq_relaxed(const u32 reg_offset)
> +{
> +	return readq_relaxed(irs_base + reg_offset);
> +}
> +
> +static void irs_writeq_relaxed(const u64 val, const u32 reg_offset)
> +{
> +	writeq_relaxed(val, irs_base + reg_offset);
> +}
> +
>  static int gicv5_irs_extract_vm_caps(const struct gic_kvm_info *info)
>  {
>  	u64 idr;
> @@ -84,16 +105,22 @@ static int gicv5_irs_extract_vm_caps(const struct gic_kvm_info *info)
>  	return 0;
>  }
>  
> +/* Forward decl for cleaner code layout */

Drop this comment. The intent is pretty obvious. And maybe move them
to the top, so that all forward declarations are grouped together.

> +static int vgic_v5_irs_assign_vmt(bool two_level, u8 vm_id_bits, phys_addr_t vmt_base);
> +static int vgic_v5_irs_clear_vmt(void);
> +
>  /*
>   * Probe for a vGICv5 compatible interrupt controller, returning 0 on success.
>   */
>  int vgic_v5_probe(const struct gic_kvm_info *info)
>  {
> +	struct vgic_v5_host_ist_caps *ist_caps;
>  	bool v5_registered = false;
>  	u64 ich_vtr_el2;
>  	int ret;
>  
>  	kvm_vgic_global_state.type = VGIC_V5;
> +	kvm_vgic_global_state.max_gic_vcpus = VGIC_V5_MAX_CPUS;
>  
>  	kvm_vgic_global_state.vcpu_base = 0;
>  	kvm_vgic_global_state.vctrl_base = NULL;
> @@ -114,13 +141,53 @@ int vgic_v5_probe(const struct gic_kvm_info *info)
>  	if (gicv5_irs_extract_vm_caps(info))
>  		goto skip_v5;
>  
> -	kvm_vgic_global_state.max_gic_vcpus = VGIC_V5_MAX_CPUS;
> +	ist_caps = vgic_v5_host_caps();
> +
> +	/*
> +	 * Even if the HW supports more per-VM vCPUs, artifically cap as we
> +	 * can't use them all.
> +	 */
> +	kvm_vgic_global_state.max_gic_vcpus = min(ist_caps->max_vpes,
> +						  VGIC_V5_MAX_CPUS);

Can this be less than 512, which we still want to support for GICv3?

> +
> +	/*
> +	 * GICv5 requires a set of tables to be allocated in order to manage
> +	 * VMs. We allocate them in advance here, which alas means that we
> +	 * already have to make a decisions regarding the maximum number of VMs
> +	 * we want to run. For now, we match the maximum number offered by the
> +	 * hardware, but this might not be a wise choice in the long term.
> +	 */
> +	ret = vgic_v5_vmt_allocate(ist_caps->two_level_vmt_support,
> +				   ist_caps->max_vms, ist_caps->vmd_size,
> +				   ist_caps->vped_size,

Why don't you just pass irs_caps to the allocator instead of teasing
out individual fields?

> +				   kvm_vgic_global_state.max_gic_vcpus);
> +	if (ret) {
> +		kvm_err("Failed to allocate the GICv5 VM tables; no GICv5 support\n");
> +		goto skip_v5;

Turn this into a hard fail.

> +	}
> +
> +	/*
> +	 * We've now allocated the VM table, but the host's IRS doesn't know
> +	 * about it yet. Provide the base address of the VMT to the IRS, as well
> +	 * as the number of ID bits that it covers and the structure used
> +	 * (linear/two-level).
> +	 */
> +	ret = vgic_v5_irs_assign_vmt(ist_caps->two_level_vmt_support,
> +				     vgic_v5_vmt_vpe_id_bits(),
> +				     vgic_v5_get_vmt_base());
> +	if (ret) {
> +		kvm_err("Failed to assign the GICv5 VM tables to the IRS; no GICv5 support\n");
> +		vgic_v5_vmt_free();
> +		goto skip_v5;
> +	}
>  
>  	vgic_v5_get_implemented_ppis();
>  
>  	ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V5);
>  	if (ret) {
>  		kvm_err("Cannot register GICv5 KVM device.\n");
> +		vgic_v5_irs_clear_vmt();
> +		vgic_v5_vmt_free();
>  		goto skip_v5;
>  	}
>  
> @@ -148,12 +215,13 @@ int vgic_v5_probe(const struct gic_kvm_info *info)
>  	ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3);
>  	if (ret) {
>  		kvm_err("Cannot register GICv3-legacy KVM device.\n");
> -		return ret;
> +		/* vGICv5 should still work */
> +		return v5_registered ? 0 : ret;
>  	}
>  
>  	/* We potentially limit the max VCPUs further than we need to here */
>  	kvm_vgic_global_state.max_gic_vcpus = min(VGIC_V3_MAX_CPUS,
> -						  VGIC_V5_MAX_CPUS);
> +						  kvm_vgic_global_state.max_gic_vcpus);
>  
>  	static_branch_enable(&kvm_vgic_global_state.gicv3_cpuif);
>  	kvm_info("GCIE legacy system register CPU interface\n");
> @@ -163,6 +231,167 @@ int vgic_v5_probe(const struct gic_kvm_info *info)
>  	return 0;
>  }
>  
> +/*
> + * Wait for completion of a change in any of IRS_VMT_BASER, IRS_VMAP_L2_VMTR,
> + * IRS_VMAP_VMR, IRS_VMAP_VPER, IRS_VMAP_VISTR, IRS_VMAP_L2_VISTR.
> + */
> +static int vgic_v5_irs_wait_for_vm_op(void)
> +{
> +	u32 statusr;
> +	int ret;
> +
> +	ret = readl_relaxed_poll_timeout_atomic(
> +		irs_base + GICV5_IRS_VMT_STATUSR, statusr,
> +		FIELD_GET(GICV5_IRS_VMT_STATUSR_IDLE, statusr), 1,
> +		USEC_PER_SEC);

nit: please don't split this line before the first parameter of the
function.

> +
> +	if (ret == -ETIMEDOUT) {
> +		pr_err_ratelimited("Time out waiting for IRS VM Op\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int vgic_v5_irs_assign_vmt(bool two_level, u8 vm_id_bits, phys_addr_t vmt_base)
> +{
> +	u64 vmt_baser;
> +	u32 vmt_cfgr;
> +
> +	vmt_baser = irs_readq_relaxed(GICV5_IRS_VMT_BASER);
> +	if (!!FIELD_GET(GICV5_IRS_VMT_BASER_VALID, vmt_baser))
> +		return -EBUSY;
> +
> +	vmt_cfgr = FIELD_PREP(GICV5_IRS_VMT_CFGR_VM_ID_BITS, vm_id_bits);
> +	if (two_level)
> +		vmt_cfgr |= FIELD_PREP(GICV5_IRS_VMT_CFGR_STRUCTURE,
> +				       GICV5_IRS_VMT_CFGR_STRUCTURE_TWO_LEVEL);
> +
> +	irs_writel_relaxed(vmt_cfgr, GICV5_IRS_VMT_CFGR);
> +
> +	/* The base address is intentionally only masked and not shifted */
> +	vmt_baser = FIELD_PREP(GICV5_IRS_VMT_BASER_VALID, true) |
> +		    (vmt_base & GICV5_IRS_VMT_BASER_ADDR);
> +	irs_writeq_relaxed(vmt_baser, GICV5_IRS_VMT_BASER);
> +
> +	return vgic_v5_irs_wait_for_vm_op();
> +}
> +
> +static int vgic_v5_irs_clear_vmt(void)
> +{
> +	irs_writeq_relaxed(0ULL, GICV5_IRS_VMT_BASER);
> +
> +	return vgic_v5_irs_wait_for_vm_op();
> +}
> +
> +static int vgic_v5_irs_vmap_l2_vmt(int vm_id)
> +{
> +	u64 vmap_l2_vmtr;
> +	int ret = 0;
> +
> +	guard(raw_spinlock)(&vm_config_lock);
> +
> +	/* Make sure that we are idle to begin with */
> +	ret = vgic_v5_irs_wait_for_vm_op();
> +	if (ret)
> +		return ret;
> +
> +	/* Mark the VM as valid */
> +	vmap_l2_vmtr = FIELD_PREP(GICV5_IRS_VMAP_L2_VMTR_VM_ID, vm_id) |
> +		       FIELD_PREP(GICV5_IRS_VMAP_L2_VMTR_M, true);
> +	irs_writeq_relaxed(vmap_l2_vmtr, GICV5_IRS_VMAP_L2_VMTR);
> +
> +	return vgic_v5_irs_wait_for_vm_op();
> +}
> +
> +static int __vgic_v5_irs_vmap_vm(int vm_id, bool unmap)
> +{
> +	u64 vmap_vmr;
> +	int ret;
> +
> +	guard(raw_spinlock)(&vm_config_lock);
> +
> +	/* Make sure that we are idle to begin with */
> +	ret = vgic_v5_irs_wait_for_vm_op();
> +	if (ret)
> +		return ret;
> +
> +	/* Mark the VM as valid */
> +	vmap_vmr = FIELD_PREP(GICV5_IRS_VMAP_VMR_VM_ID, vm_id) |
> +		   FIELD_PREP(GICV5_IRS_VMAP_VMR_U, unmap) |
> +		   FIELD_PREP(GICV5_IRS_VMAP_VMR_M, true);
> +	irs_writeq_relaxed(vmap_vmr, GICV5_IRS_VMAP_VMR);
> +
> +	return vgic_v5_irs_wait_for_vm_op();
> +}

There is a pattern here:

static int do_something(...)
{
	int ret
	guard(raw_spinlock)(&vm_config_lock);

	/* Make sure that we are idle to begin with */
	ret = vgic_v5_irs_wait_for_vm_op();
	if (ret)
		return ret;

	[do the something we came here for]

	return vgic_v5_irs_wait_for_vm_op();
}

Surely this can be turned into a helper that avoids having that
boilerplate code in each and every function.

> +
> +static int vgic_v5_irs_set_vm_valid(int vm_id)
> +{
> +	return __vgic_v5_irs_vmap_vm(vm_id, false);
> +}
> +
> +static int vgic_v5_irs_set_vm_invalid(int vm_id)
> +{
> +	return __vgic_v5_irs_vmap_vm(vm_id, true);
> +}
> +
> +static int __vgic_v5_irs_update_vist_validity(int vm_id, bool spi_ist, bool unmap)
> +{
> +	u8 type = spi_ist ? 0b011 : 0b010;
> +	u64 vmap_vistr;
> +	int ret;
> +
> +	guard(raw_spinlock)(&vm_config_lock);
> +
> +	/* Make sure that we are idle to begin with */
> +	ret = vgic_v5_irs_wait_for_vm_op();
> +	if (ret)
> +		return ret;
> +
> +	/* Mark the IST as valid */
> +	vmap_vistr = FIELD_PREP(GICV5_IRS_VMAP_VISTR_TYPE, type) |
> +		     FIELD_PREP(GICV5_IRS_VMAP_VISTR_VM_ID, vm_id) |
> +		     FIELD_PREP(GICV5_IRS_VMAP_VISTR_U, unmap) |
> +		     FIELD_PREP(GICV5_IRS_VMAP_VISTR_M, true);
> +	irs_writeq_relaxed(vmap_vistr, GICV5_IRS_VMAP_VISTR);
> +
> +	return vgic_v5_irs_wait_for_vm_op();
> +}
> +
> +static int vgic_v5_irs_set_vist_valid(int vm_id, bool spi_ist)
> +{
> +	return __vgic_v5_irs_update_vist_validity(vm_id, spi_ist, false);
> +}
> +
> +/* Note: We currently do not use this as we rely on the VM becoming invalid. */
> +static int vgic_v5_irs_set_vist_invalid(int vm_id, bool spi_ist)
> +{
> +	return __vgic_v5_irs_update_vist_validity(vm_id, spi_ist, true);
> +}
> +
> +static int vgic_v5_db_set_vcpu_affinity(struct irq_data *data, void *vcpu_info)
> +{
> +	struct vgic_v5_vm *vm = data->domain->host_data;
> +	struct gicv5_cmd_info *cmd_info = vcpu_info;
> +
> +	switch (cmd_info->cmd_type) {
> +	case VMT_L2_MAP:
> +		return vgic_v5_irs_vmap_l2_vmt(vm->vm_id);
> +	case VMTE_MAKE_VALID:
> +		return vgic_v5_irs_set_vm_valid(vm->vm_id);
> +	case VMTE_MAKE_INVALID:
> +		return vgic_v5_irs_set_vm_invalid(vm->vm_id);
> +	case SPI_VIST_MAKE_VALID:
> +		return vgic_v5_irs_set_vist_valid(vm->vm_id, true);
> +	case LPI_VIST_MAKE_VALID:
> +		return vgic_v5_irs_set_vist_valid(vm->vm_id, false);
> +	case LPI_VIST_MAKE_INVALID:
> +		return vgic_v5_irs_set_vist_invalid(vm->vm_id, false);
> +	default:
> +		return -EINVAL;
> +	}
> +}

This function should be introduced ages ago, as soon as you start
issuing vcpu_set_affinity() calls.

> +
>  /*
>   * This set of irq_chip functions is specific for doorbells.
>   */
> @@ -174,6 +403,7 @@ static struct irq_chip vgic_v5_db_irq_chip = {
>  	.irq_set_affinity = irq_chip_set_affinity_parent,
>  	.irq_get_irqchip_state = irq_chip_get_parent_state,
>  	.irq_set_irqchip_state = irq_chip_set_parent_state,
> +	.irq_set_vcpu_affinity = vgic_v5_db_set_vcpu_affinity,
>  	.flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE |
>  		 IRQCHIP_MASK_ON_SUSPEND,
>  };
> diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h
> index ccec0a045927c..ff5ad653252d2 100644
> --- a/include/linux/irqchip/arm-gic-v5.h
> +++ b/include/linux/irqchip/arm-gic-v5.h
> @@ -87,6 +87,12 @@
>  #define GICV5_IRS_IST_CFGR		0x0190
>  #define GICV5_IRS_IST_STATUSR		0x0194
>  #define GICV5_IRS_MAP_L2_ISTR		0x01c0
> +#define GICV5_IRS_VMT_BASER		0x0200
> +#define GICV5_IRS_VMT_CFGR		0x0210
> +#define GICV5_IRS_VMT_STATUSR		0x0214
> +#define GICV5_IRS_VMAP_L2_VMTR		0x02c0
> +#define GICV5_IRS_VMAP_VMR		0x02c8
> +#define GICV5_IRS_VMAP_VISTR		0x02d0
>  
>  #define GICV5_IRS_IDR0_VIRT		BIT(6)
>  
> @@ -181,6 +187,30 @@
>  
>  #define GICV5_IRS_MAP_L2_ISTR_ID	GENMASK(23, 0)
>  
> +#define GICV5_IRS_VMT_BASER_ADDR	GENMASK_ULL(51, 3)
> +#define GICV5_IRS_VMT_BASER_ADDR_SHIFT	3ULL
> +#define GICV5_IRS_VMT_BASER_VALID	BIT_ULL(0)
> +
> +#define GICV5_IRS_VMT_CFGR_STRUCTURE_TWO_LEVEL	0b1
> +#define GICV5_IRS_VMT_CFGR_STRUCTURE_LINEAR	0b0
> +
> +#define GICV5_IRS_VMT_CFGR_STRUCTURE	BIT(16)
> +#define GICV5_IRS_VMT_CFGR_VM_ID_BITS	GENMASK(4, 0)
> +
> +#define GICV5_IRS_VMT_STATUSR_IDLE	BIT(0)
> +
> +#define GICV5_IRS_VMAP_L2_VMTR_M	BIT_ULL(63)
> +#define GICV5_IRS_VMAP_L2_VMTR_VM_ID	GENMASK_ULL(15, 0)
> +
> +#define GICV5_IRS_VMAP_VMR_M		BIT_ULL(63)
> +#define GICV5_IRS_VMAP_VMR_U		BIT_ULL(62)
> +#define GICV5_IRS_VMAP_VMR_VM_ID	GENMASK_ULL(15, 0)
> +
> +#define GICV5_IRS_VMAP_VISTR_M		BIT_ULL(63)
> +#define GICV5_IRS_VMAP_VISTR_U		BIT_ULL(62)
> +#define GICV5_IRS_VMAP_VISTR_VM_ID	GENMASK_ULL(47, 32)
> +#define GICV5_IRS_VMAP_VISTR_TYPE	GENMASK_ULL(31, 29)
> +
>  #define GICV5_ISTL1E_VALID		BIT_ULL(0)
>  #define GICV5_IRS_ISTL1E_SIZE		8UL
>  

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.


^ permalink raw reply

* Re: [PATCH v21 08/13] mfd: core: Add firmware-node support to MFD cells
From: Shivendra Pratap @ 2026-04-29 16:10 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: linux-pm, linux-kernel, linux-arm-msm, linux-arm-kernel,
	devicetree, Florian Fainelli, Krzysztof Kozlowski,
	Dmitry Baryshkov, Mukesh Ojha, Andre Draszik, Greg Kroah-Hartman,
	Kathiravan Thirumoorthy, Srinivas Kandagatla, Sebastian Reichel,
	Mark Rutland, Lorenzo Pieralisi, Rafael J. Wysocki,
	Daniel Lezcano, Christian Loehle, Ulf Hansson, Lee Jones,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Arnd Bergmann, Souvik Chakravarty, Andy Yan,
	Matthias Brugger, John Stultz, Moritz Fischer, Sudeep Holla
In-Reply-To: <CAMRc=Md_OON4cAGcJ=jDu=VY_mw0qa8HPz4VCDxYLpr7JMxLRw@mail.gmail.com>



On 29-04-2026 15:20, Bartosz Golaszewski wrote:
> On Wed, Apr 29, 2026 at 11:46 AM Shivendra Pratap
> <shivendra.pratap@oss.qualcomm.com> wrote:
>>
>>>>
>>>> If set, it would be called from MFD core and would do the name lookup
>>>> and return the fwnode which would then be assigned to the cell device?
>>>
>>
>> Or should we just drop this patch for fwnode in mfd-core?
>>
>> Export a new call in reboot-mode framework as:
>> reboot_mode_register_node(struct reboot_mode_driver *reboot,
>>                     struct device_node *np / fwnode)
>>
>> Use the parent node in psci-reboot-mode and find reboot-mode and call
>> reboot_mode_register_node?
>>
> 
> No, I think the reboot-mode device (as pointed to by the dev field of
> struct reboot_mode_driver) should be associated with the right fwnode
> from the start so it should be handled in MFD core.

Ack. something like should be fine?

drivers/mfd/mfd-core.c :
+       if (!pdev->dev.fwnode && cell->get_fwnode) {
+               fwnode = cell->get_fwnode(parent);
+               if (fwnode)
+                       device_set_node(&pdev->dev, fwnode);
+       }

drivers/mfd/psci-mfd.c (goes next patch):
+static struct fwnode_handle *psci_reboot_mode_get_fwnode(struct device 
*parent)
+{
+       return fwnode_get_named_child_node(dev_fwnode(parent), 
"reboot-mode");
+}

--

thanks,
Shivendra


^ permalink raw reply

* Re: [PATCH 3/4] soc: qcom: Make important drivers default
From: Krzysztof Kozlowski @ 2026-04-29 16:10 UTC (permalink / raw)
  To: Konrad Dybcio, Bjorn Andersson, Konrad Dybcio
  Cc: linux-arm-msm, linux-kernel, linux-arm-kernel
In-Reply-To: <48c84ad0-7d6b-4e2e-8bd3-511ec679462e@oss.qualcomm.com>

On 29/04/2026 11:06, Konrad Dybcio wrote:
> On 4/29/26 10:56 AM, Krzysztof Kozlowski wrote:
>> The drivers for Qualcomm SoC components are covering a basic or
>> fundamental SoC blocks.  Usually they are required for booting or to
>> achieve basic expected functionality when running Linux.  These drivers
>> do not represent any sort of buses visible to the board
>> designers/configurators, thus they should be always enabled, regardless
>> how SoC is used in the final board.
>>
>> Kernel configuration should not ask users choice of drivers when that
>> choice is obvious and known to the developers that answer should be
>> 'yes' or 'module'.
>>
>> Switch most of the Qualcomm SoC drivers to a default 'yes' or
>> 'module' for ARCH_QCOM, to match existing defconfig usage.
>>
>> This has no impact on arm64 defconfig, arm qcom_defconfig and arm
>> multi_v7_defconfig.
>>
>> The change will however enable by default all drivers for arm or arm64
>> COMPILE_TEST builds, whenever ARCH_QCOM is selected, which feels
>> logical: if one selects ARCH_QCOM then probably by default wants to
>> build test it entirely.  Kernels with COMPILE_TEST are not supposed to
>> be used for booting.
>>
>> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
>> ---
> 
> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> 
> Please also add:
> 
> QCOM_RMTFS_MEM (required for modem)

It's in the patch.

> QCOM_SPM (cpufreq-adjacent on some platforms)

I assume only for arm, because none of arm64 compatibles are present in
upstream DTS.

> 
> Konrad


Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v8 09/58] perf evlist: Add reference count
From: Ian Rogers @ 2026-04-29 16:17 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: acme, adrian.hunter, alice.mei.rogers, dapeng1.mi, james.clark,
	leo.yan, linux-arm-kernel, linux-kernel, linux-perf-users, mingo,
	peterz, tmricht
In-Reply-To: <afGyYi01XSNIS-A2@z2>

On Wed, Apr 29, 2026 at 12:25 AM Namhyung Kim <namhyung@kernel.org> wrote:
>
> On Tue, Apr 28, 2026 at 12:18:14AM -0700, Ian Rogers wrote:
> > This a no-op for most of the perf tool. The reference count is set to
> > 1 at allocation, the put will see the 1, decrement it and perform the
> > delete. The purpose for adding the reference count is for the python
> > code. Prior to this change the python code would clone evlists, but
> > this has issues if events are opened, etc. This change adds a
> > reference count for the evlists and a later change will add it to
> > evsels. The combination is needed for the python code to operate
> > correctly (not hit asserts in the evsel clone), but the changes are
> > broken apart for the sake of smaller patches.
>
> Can we keep the existing evlist__delete() code?  IIUC most parts of the
> code don't need to worry about python.  Then do we really need to
> convert them all?  Maybe we can convert only the relevant codes.

Are you thinking of evlist__delete being something like:
```
void evlist__delete(struct evlist *evlist)
{
   assert(refcount_read(evlist__refcnt(evlist)) == 1);
   evlist__put(evlist);
}
```
This would allow the existing delete cases to remain and minimize
churn, but it creates an opportunity for a runtime assertion failure.
I'm not sure what the codebase gains long-term from this as having
both `put` and `delete` feels like an unnecessary choice for the user.

> Also I think it's better to split evlist__put() conversion and python
> code changes.

So the existing Python code embeds an evlist inside a Python object. I
don't see a way to add the reference count and keep the embedding
without inventing a 3rd way of handling Python evlists, which would be
as complex as just using the reference counts. We could remove the
embedded evlist and use evlist__new/delete, but the code wouldn't be
functional.

Thanks,
Ian

> Thanks,
> Namhyung
>
> >
> > Assisted-by: Gemini:gemini-3.1-pro-preview
> > Signed-off-by: Ian Rogers <irogers@google.com>
> > ---
> > v2: Added evlist__put to pyrf_evlist__init in case init is called more
> >     than once.
> >
> >     I double-checked trace__replay() and confirmed that trace->evlist
> >     is not assigned to session->evlist in that function.
> >     trace__replay creates a new session and uses its own evlist for
> >     processing file events, leaving trace->evlist pointing to the
> >     empty list created at startup. Therefore, the
> >     evlist__put(trace->evlist) call in trace__exit() is safe and
> >     correct to avoid leaking that empty list.
> >
> > v7:
> > - Added pyrf_evlist__new to zero-initialize pevlist->evlist to fix
> >   crash on re-initialization.


^ 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