Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] arm64: traps: fix userspace cache maintenance emulation on a tagged pointer
From: Andre Przywara @ 2017-04-21 10:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1492712234-4950-2-git-send-email-kristina.martsenko@arm.com>

Hi Kristina,

On 20/04/17 19:17, Kristina Martsenko wrote:
> When we emulate userspace cache maintenance in the kernel, we can
> currently send the task a SIGSEGV even though the maintenance was done
> on a valid address. This happens if the address has a non-zero address
> tag, and happens to not be mapped in.
> 
> When we get the address from a user register, we don't currently remove
> the address tag before performing cache maintenance on it. If the
> maintenance faults, we end up in either __do_page_fault, where find_vma
> can't find the VMA if the address has a tag, or in do_translation_fault,
> where the tagged address will appear to be above TASK_SIZE. In both
> cases, the address is not mapped in, and the task is sent a SIGSEGV.

Right, well spotted!
So thanks for the patch, which I think is correct. But ...

> This patch removes the tag from the address before using it. With this
> patch, the fault is handled correctly, the address gets mapped in, and
> the cache maintenance succeeds.

Looking more closely at this code, I see that we actually don't use the
address parameter in the force_signal_inject() function. Instead we
always put the PC address into the siginfo structure, which is wrong in
case this SEGV is triggered by an invalid address of a cache maintenance
operation.

I made a simple patch to fix this (using the address argument and
explicitly passing the PC in when we fault on an invalid instruction).

But now we would pass the untagged address back into userland. I am not
sure this is a real problem, since we don't promise anything in case of
tagged pointers, if I got this correctly.

But also our untagged_addr() macro seems to not cover all cases
correctly, for instance passing in 0x00ffffffffff5678 (which is an
invalid, but untagged address) would extend to some probably valid
kernel pointer. And although this would fail our user space address
check, we would return a wrong address (with all the upper bits being 1)
in siginfo.

Do we care about this?
What would be the best fix for the untagged_addr macro? Is that macro
actually the proper place to fix this issue?

Cheers,
Andre.

> As a second bug, if cache maintenance (correctly) fails on an invalid
> tagged address, the address gets passed into arm64_notify_segfault,
> where find_vma fails to find the VMA due to the tag, and the wrong
> si_code may be sent as part of the siginfo_t of the segfault. With this
> patch, the correct si_code is sent.
> 
> Fixes: 7dd01aef0557 ("arm64: trap userspace "dc cvau" cache operation on errata-affected core")
> Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
> ---
> 
> Note that patch #3 would also fix the first bug (incorrect segfault),
> but not the second (wrong si_code), hence this patch.
> 
>  arch/arm64/kernel/traps.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
> index e52be6aa44ee..45c8eca951bc 100644
> --- a/arch/arm64/kernel/traps.c
> +++ b/arch/arm64/kernel/traps.c
> @@ -443,7 +443,7 @@ int cpu_enable_cache_maint_trap(void *__unused)
>  }
>  
>  #define __user_cache_maint(insn, address, res)			\
> -	if (untagged_addr(address) >= user_addr_max()) {	\
> +	if (address >= user_addr_max()) {			\
>  		res = -EFAULT;					\
>  	} else {						\
>  		uaccess_ttbr0_enable();				\
> @@ -469,7 +469,7 @@ static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
>  	int crm = (esr & ESR_ELx_SYS64_ISS_CRM_MASK) >> ESR_ELx_SYS64_ISS_CRM_SHIFT;
>  	int ret = 0;
>  
> -	address = pt_regs_read_reg(regs, rt);
> +	address = untagged_addr(pt_regs_read_reg(regs, rt));
>  
>  	switch (crm) {
>  	case ESR_ELx_SYS64_ISS_CRM_DC_CVAU:	/* DC CVAU, gets promoted */
> 

^ permalink raw reply

* [PATCH v3 7/8] arm64: exception: handle asynchronous SError interrupt
From: Xiongfeng Wang @ 2017-04-21 10:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1490869877-118713-8-git-send-email-xiexiuqi@huawei.com>

Hi XiuQi,

On 2017/3/30 18:31, Xie XiuQi wrote:
> Error Synchronization Barrier (ESB; part of the ARMv8.2 Extensions)
> is used to synchronize Unrecoverable errors. That is, containable errors
> architecturally consumed by the PE and not silently propagated.
> 
> With ESB it is generally possible to isolate an unrecoverable error
> between two ESB instructions. So, it's possible to recovery from
> unrecoverable errors reported by asynchronous SError interrupt.
> 
> If ARMv8.2 RAS Extension is not support, ESB is treated as a NOP.
> 
> Signed-off-by: Xie XiuQi <xiexiuqi@huawei.com>
> Signed-off-by: Wang Xiongfeng <wangxiongfengi2@huawei.com>
> ---
>  arch/arm64/Kconfig           | 16 ++++++++++
>  arch/arm64/include/asm/esr.h | 14 +++++++++
>  arch/arm64/kernel/entry.S    | 70 ++++++++++++++++++++++++++++++++++++++++++--
>  arch/arm64/kernel/traps.c    | 54 ++++++++++++++++++++++++++++++++--
>  4 files changed, 150 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 859a90e..7402175 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -911,6 +911,22 @@ endmenu
>  
>  menu "ARMv8.2 architectural features"
>  
> +config ARM64_ESB
> +	bool "Enable support for Error Synchronization Barrier (ESB)"
> +	default n
> +	help
> +	  Error Synchronization Barrier (ESB; part of the ARMv8.2 Extensions)
> +	  is used to synchronize Unrecoverable errors. That is, containable errors
> +	  architecturally consumed by the PE and not silently propagated.
> +
> +	  Without ESB it is not generally possible to isolate an Unrecoverable
> +	  error because it is not known which instruction generated the error.
> +
> +	  Selecting this option allows inject esb instruction before the exception
> +	  change. If ARMv8.2 RAS Extension is not support, ESB is treated as a NOP.
> +
> +	  Note that ESB instruction can introduce slight overhead, so say N if unsure.
> +
>  config ARM64_UAO
>  	bool "Enable support for User Access Override (UAO)"
>  	default y
> diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
> index f20c64a..22f9c90 100644
> --- a/arch/arm64/include/asm/esr.h
> +++ b/arch/arm64/include/asm/esr.h
> @@ -106,6 +106,20 @@
>  #define ESR_ELx_AR 		(UL(1) << 14)
>  #define ESR_ELx_CM 		(UL(1) << 8)
>  
> +#define ESR_Elx_DFSC_SEI	(0x11)
> +
> +#define ESR_ELx_AET_SHIFT	(10)
> +#define ESR_ELx_AET_MAX		(7)
> +#define ESR_ELx_AET_MASK	(UL(7) << ESR_ELx_AET_SHIFT)
> +#define ESR_ELx_AET(esr)	(((esr) & ESR_ELx_AET_MASK) >> ESR_ELx_AET_SHIFT)
> +
> +#define ESR_ELx_AET_UC		(0)
> +#define ESR_ELx_AET_UEU		(1)
> +#define ESR_ELx_AET_UEO		(2)
> +#define ESR_ELx_AET_UER		(3)
> +#define ESR_ELx_AET_CE		(6)
> +
> +
>  /* ISS field definitions for exceptions taken in to Hyp */
>  #define ESR_ELx_CV		(UL(1) << 24)
>  #define ESR_ELx_COND_SHIFT	(20)
> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
> index 43512d4..d8a7306 100644
> --- a/arch/arm64/kernel/entry.S
> +++ b/arch/arm64/kernel/entry.S
> @@ -69,7 +69,14 @@
>  #define BAD_FIQ		2
>  #define BAD_ERROR	3
>  
> +	.arch_extension ras
> +
>  	.macro	kernel_entry, el, regsize = 64
because we also want to take SEI exception in kernel space, we may need to unmask SEI in kernel_entry.
> +#ifdef CONFIG_ARM64_ESB
> +	.if	\el == 0
> +	esb
> +	.endif
> +#endif
>  	sub	sp, sp, #S_FRAME_SIZE
>  	.if	\regsize == 32
>  	mov	w0, w0				// zero upper 32 bits of x0
> @@ -208,6 +215,7 @@ alternative_else_nop_endif
>  #endif
>  
>  	.if	\el == 0
> +	msr	daifset, #0xF			// Set flags
>  	ldr	x23, [sp, #S_SP]		// load return stack pointer
>  	msr	sp_el0, x23
>  #ifdef CONFIG_ARM64_ERRATUM_845719
> @@ -226,6 +234,15 @@ alternative_else_nop_endif
>  
>  	msr	elr_el1, x21			// set up the return data
>  	msr	spsr_el1, x22
> +
> +#ifdef CONFIG_ARM64_ESB
> +	.if \el == 0
> +	esb					// Error Synchronization Barrier
> +	mrs	x21, disr_el1			// Check for deferred error
> +	tbnz	x21, #31, el1_sei
> +	.endif
> +#endif
> +
>  	ldp	x0, x1, [sp, #16 * 0]
>  	ldp	x2, x3, [sp, #16 * 1]
>  	ldp	x4, x5, [sp, #16 * 2]
> @@ -318,7 +335,7 @@ ENTRY(vectors)
>  	ventry	el1_sync_invalid		// Synchronous EL1t
>  	ventry	el1_irq_invalid			// IRQ EL1t
>  	ventry	el1_fiq_invalid			// FIQ EL1t
> -	ventry	el1_error_invalid		// Error EL1t
> +	ventry	el1_error			// Error EL1t
>  
>  	ventry	el1_sync			// Synchronous EL1h
>  	ventry	el1_irq				// IRQ EL1h
> @@ -328,7 +345,7 @@ ENTRY(vectors)
>  	ventry	el0_sync			// Synchronous 64-bit EL0
>  	ventry	el0_irq				// IRQ 64-bit EL0
>  	ventry	el0_fiq_invalid			// FIQ 64-bit EL0
> -	ventry	el0_error_invalid		// Error 64-bit EL0
> +	ventry	el0_error			// Error 64-bit EL0

for the situation 'Current EL with SPx', we also need to change the sError vector to el1_error.
>  
>  #ifdef CONFIG_COMPAT
>  	ventry	el0_sync_compat			// Synchronous 32-bit EL0
> @@ -508,12 +525,31 @@ el1_preempt:
>  	ret	x24
>  #endif
>  
> +	.align	6
> +el1_error:
> +	kernel_entry 1
> +el1_sei:
> +	/*
> +	 * asynchronous SError interrupt from kernel
> +	 */
> +	mov	x0, sp
> +	mrs	x1, esr_el1
> +	mov	x2, #1				// exception level of SEI generated
> +	b	do_sei
> +ENDPROC(el1_error)
> +
> +


Thanks,

Wang Xiongfeng

^ permalink raw reply

* [PATCH 4/5] Hot-remove implementation for arm64
From: Andrea Reale @ 2017-04-21 10:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <535ba380-56e8-db3d-25c5-14d51e48105f@redhat.com>

Hi all,

thanks for taking the time to comment. Replies in-line.

On Wed, Apr 19, 2017 at 08:53:13AM -0700, Laura Abbott wrote:
> On 04/18/2017 11:48 AM, Ard Biesheuvel wrote:
> >On 18 April 2017 at 19:21, Mark Rutland <mark.rutland@arm.com> wrote:
> >>On Fri, Apr 14, 2017 at 03:01:58PM +0100, Andrea Reale wrote:

[...]

> >>
> >> From a quick scan, I see that it's necessary to use pgtable_page_ctor()
> >>for pages that will be used for userspace page tables, but it's not
> >>clear to me if it's ever necessary for pages used for kernel page
> >>tables.
> >>
> >>If it is, we appear to have a bug on arm64.
> >>
> >>Laura, Ard, thoughts?
> >>
> >
> >The generic apply_to_page_range() will expect the PTE lock to be
> >initialized for page table pages that are not part of init_mm. For
> >arm64, that is precisely efi_mm as far as I am aware. For EFI, the
> >locking is unnecessary but does no harm (the permissions are set once
> >via apply_to_page_range() at boot), so I added this call when adding
> >support for strict permissions in EFI rt services mappings.
> >
> >So I think it is appropriate for create_pgd_mapping() to be in charge
> >of calling the ctor(). We simply have no destroy_pgd_mapping()
> >counterpart that would be the place for the dtor() call, given that we
> >never take down EFI rt services mappi >
> >Whether it makes sense or not to lock/unlock in apply_to_page_range()
> >is something I did not spend any brain cycles on at the time.
> >
> 
> Agreed there shouldn't be a problem right now. I do think the locking is
> appropriate in apply_to_page_range given what other functions also get
> locked.
> 
> I really wish this were less asymmetrical though since it get hard
> to reason about. It looks like hotplug_paging will call the ctor,
> so is there an issue with calling hot-remove on memory that was once
> hot-added or is that not a concern?
> 
> Thanks,
> Laura

I think the confusion comes from the fact that, in hotplug_paging, we are
passing pgd_pgtable_alloc as the page allocator for __create_pgd_mapping,
which always calls the ctor.

If I got things right (but, please, correct me if I am wrong), we don't
need to get the pte_lock that the ctor gets since - in hotplug - we are
adding to init_mm.

Moreover, I am just realizing that calling the dtor while hot-removing
might create problems when removing memory that *was not* previously
hotplugged, as we are calling a dtor on something that was never
ctor'ed. Is that what you were hinting at, Laura?

Thanks and best regards,
Andrea

^ permalink raw reply

* [PATCH 4/5] Hot-remove implementation for arm64
From: Andrea Reale @ 2017-04-21 10:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170418182125.GL17866@leverpostej>

Hi all,

Thanks Mark, Ard and Laura for your comments. Replies in-line.

On Tue, Apr 18, 2017 at 07:21:26PM +0100, Mark Rutland wrote:
[...]
> >  
> > The name direct is inherited directly from the x86_64 hot remove code.
> > It serves to distinguish if we are removing either a pagetable page that
> > is mapping to the direct mapping space (I think it is called also linear
> > mapping area somewhere else) or a pagetable page or a data page 
> > from vmemmap.
> 
> FWIW, I've largely heard the folk call that the "linear mapping", and
> x86 folk call that the "direct mapping". The two are interchangeable.
> 

Thanks for the clarification; We'll just call it "linear mapping" then.

> > In this specific set of functions, the flag is needed because the various
> > alloc_init_p*d used for allocating entries for direct memory mapping
> > rely on pgd_pgtable_alloc, which in its turn calls  pgtable_page_ctor;
> > hence, we need to call the dtor. 
> 
> AFAICT, that's not true for the arm64 linear map, since that's created
> with our early_pgtable_alloc(), which doesn't call pgtable_page_ctor().
> 
> Judging by commit:
> 
>   1378dc3d4ba07ccd ("arm64: mm: run pgtable_page_ctor() on non-swapper
>                      translation table pages")
> 
> ... we only do this for non-swapper page tables.
> 
> > On the contrary, vmemmap entries are created using vmemmap_alloc_block
> > (from within vmemmap_populate), which does not call pgtable_page_ctor
> > when allocating pages.
> > 
> > I am not sure I understand why the pgtable_page_ctor is not called when
> > allocating vmemmap page tables, but that's the current situation.
> 
> From a quick scan, I see that it's necessary to use pgtable_page_ctor()
> for pages that will be used for userspace page tables, but it's not
> clear to me if it's ever necessary for pages used for kernel page
> tables.
> 
> If it is, we appear to have a bug on arm64.
> 
> Laura, Ard, thoughts?
> 

More comments on that as a separate reply to Laura's and Ard's messages.

[...]

> > 
> > I guess that the safe approach would be something along the lines:
> > 1. clear the page table 
> > 2. flush the tlbs
> > 3. free the page
> 
> Yes. That's the sequence we follow elsewhere.
> 
> > When I am flushing intermediate p*d entries, would it be
> > more appropriate to use something like __flush_tlb_pgtable() to clear
> > cached partial walks rather than flushing the whole table? I mean,
> > hot-remove is not going to be a frequent operation anyway, so I don't
> > think that flushing the whole tlb would be a great deal of harm
> > anyway.
> 
> Using __flush_tlb_pgtable() sounds sane to me. That's what we do when
> tearing down user mappings.
> 
> > My question at this point would be: how come that the code structure above
> > works for x86_64 hot-remove?
> 
> I don't know enough about x86 to say.
> 
> > My assumption, when I was writing this, was
> > that there would be no problem since this code is called when we are sure
> > that all the memory mapped by these entries has been already offlined,
> > so nobody should be using those VAs anyway (also considering that there
> > cannot be any two mem hot-plug/remove actions running concurrently).
> > Is that correct?
> 
> The problem is that speculation, Out-of-Order execution, HW prefetching,
> and other such things *can* result in those VAs being accessed,
> regardless of what code explicitly does in a sequential execution.
> 
> If any table relevant to one of those VAs has been freed (and
> potentially modified by the allocator, or in another thread), it's
> possible that the CPU performs a page table walk and sees junk as a
> valid page table entry. As a result, a number of bad things can happen.
> 
> If the entry was a pgd, pud, or pmd, the CPU may try to continue to walk
> to the relevant pte, accessing a junk address. That could change the
> state of MMIO, trigger an SError, etc, or allocate more junk into the
> TLBs.
> 
> For any level, the CPU might allocate the entry into a TLB, regardless
> of whether an existing entry existed. The new entry can conflict with
> the existing one, either leading to a TLB conflict abort, or to the TLB
> returning junk for that address. Speculation and so on can now access
> junk based on that, etc.
> 
> [...]
> 

Thanks, we'll just clear it the proper way.

[...]

> > > > +
> > > > +		if (!pte_present(*pte))
> > > > +			continue;
> > > > +
> > > > +		if (PAGE_ALIGNED(addr) && PAGE_ALIGNED(next)) {
> > > 
> > > When would those addresses *not* be page-aligned? By construction, next
> > > must be. Unplugging partial pages of memory makes no sense, so surely
> > > addr is page-aligned when passed in?
> > 
> > The issue here is that this function is called in one of two cases:
> > 1. to clear pagetables of directly mapped (linear) memory 
> > 2. Pagetables (and corresponding data pages) for vmemmap. 
> > 
> > It is my understanding that, in the second case, we might be clearing
> > only part of the page content (i.e, only a few struct pages). Note that
> > next is page aligned by construction only if next <= end.
> 
> Ok. A comment to that effect immediately above this check would be
> helpful.
> 

Ok, thanks.

> > > > +			/*
> > > > +			 * Do not free direct mapping pages since they were
> > > > +			 * freed when offlining, or simplely not in use.
> > > > +			 */
> > > > +			if (!direct)
> > > > +				free_pagetable(pte_page(*pte), 0, direct);
> > > > +
> > > > +			/*
> > > > +			 * This spin lock could be only
> > > > +			 * taken in _pte_aloc_kernel in
> > > > +			 * mm/memory.c and nowhere else
> > > > +			 * (for arm64). Not sure if the
> > > > +			 * function above can be called
> > > > +			 * concurrently. In doubt,
> > > > +			 * I am living it here for now,
> > > > +			 * but it probably can be removed.
> > > > +			 */
> > > > +			spin_lock(&init_mm.page_table_lock);
> > > > +			pte_clear(&init_mm, addr, pte);
> > > > +			spin_unlock(&init_mm.page_table_lock);
> > > > +		} else {
> > > > +			/*
> > > > +			 * If we are here, we are freeing vmemmap pages since
> > > > +			 * direct mapped memory ranges to be freed are aligned.
> > > > +			 *
> > > > +			 * If we are not removing the whole page, it means
> > > > +			 * other page structs in this page are being used and
> > > > +			 * we canot remove them. So fill the unused page_structs
> > > > +			 * with 0xFD, and remove the page when it is wholly
> > > > +			 * filled with 0xFD.
> > > > +			 */
> > > > +			memset((void *)addr, PAGE_INUSE, next - addr);
> > > 
> > > What's special about 0xFD?
> > 
> > Just used it as a constant symmetrically to x86_64 code.
> > 
> > > Why do we need to mess with the page array in this manner? Why can't we
> > > detect when a range is free by querying memblock, for example?
> > 
> > I am not sure I get your suggestion. I guess that the logic here
> > is that I cannot be sure that I can free the full page because other
> > entries might be in use for active vmemmap mappings. So we just "mark"
> > the unused once and only free the page when all of it is marked. See
> > again commit ae9aae9eda2db71, where all this comes from.
> 
> I understood that this is deferring freeing until a whole page of struct
> pages has been freed.
> 
> My concern is that filling the unused memory with an array of junk chars
> feels like a hack. We don't do this at the edges when we allocate
> memblock today, AFAICT, so this doesn't seem complete.
> 
> Is there no "real" datastructure we can use to keep track of what memory
> is present? e.g. memblock?
> 
> [...]

We could add a MEMBLOCK_VMEMMAP_UNUSED flag in memblock and mark the
partially unused memblock range with that instead of using the 0xFD
hack. Eventually that might even be backported to x86. Is that what
you are suggesting? I am not confident we can reuse an existing flag
for the purpose without breaking something else.

[...]

> > > If that's the case, we'll *need* to split the pmd, which we can't do on
> > > live page tables.
> > 
> > Please, see below.
> > 
> > > > +				if (!direct) {
> > > > +					free_pagetable(pmd_page(*pmd),
> > > > +						get_order(PMD_SIZE), direct);
> > > > +				}
> > > > +				/*
> > > > +				 * This spin lock could be only
> > > > +				 * taken in _pte_aloc_kernel in
> > > > +				 * mm/memory.c and nowhere else
> > > > +				 * (for arm64). Not sure if the
> > > > +				 * function above can be called
> > > > +				 * concurrently. In doubt,
> > > > +				 * I am living it here for now,
> > > > +				 * but it probably can be removed.
> > > > +				 */
> > > > +				spin_lock(&init_mm.page_table_lock);
> > > > +				pmd_clear(pmd);
> > > > +				spin_unlock(&init_mm.page_table_lock);
> > > > +			} else {
> > > > +				/* If here, we are freeing vmemmap pages. */
> > > > +				memset((void *)addr, PAGE_INUSE, next - addr);
> > > > +
> > > > +				page_addr = page_address(pmd_page(*pmd));
> > > > +				if (!memchr_inv(page_addr, PAGE_INUSE,
> > > > +						PMD_SIZE)) {
> > > > +					free_pagetable(pmd_page(*pmd),
> > > > +						get_order(PMD_SIZE), direct);
> > > > +
> > > > +					/*
> > > > +					 * This spin lock could be only
> > > > +					 * taken in _pte_aloc_kernel in
> > > > +					 * mm/memory.c and nowhere else
> > > > +					 * (for arm64). Not sure if the
> > > > +					 * function above can be called
> > > > +					 * concurrently. In doubt,
> > > > +					 * I am living it here for now,
> > > > +					 * but it probably can be removed.
> > > > +					 */
> > > > +					spin_lock(&init_mm.page_table_lock);
> > > > +					pmd_clear(pmd);
> > > > +					spin_unlock(&init_mm.page_table_lock);
> > > > +				}
> > > 
> > > I don't think this is correct.
> > > 
> > > If we're getting rid of a partial pmd, we *must* split the pmd.
> > > Otherwise, we're leaving bits mapped that should not be. If we split the
> > > pmd, we can free the individual pages as we would for a non-section
> > > mapping.
> > > 
> > > As above, we can't split block entries within live tables, so that will
> > > be painful at best.
> > > 
> > > If we can't split a pmd, hen we cannot free a partial pmd, and will need
> > > to reject request to do so.
> > > 
> > > The same comments (with s/pmu/pud/, etc) apply for the higher level
> > > remove_p*d_table functions.
> > > 
> > > [...]
> > 
> > This only happens when we are clearing vmemmap memory. 
> 
> Is that definitely the case?
> 
> Currently, I can't see what prevents adding 2M of memory, and then
> removing the first 4K of that. We'll use a 2M section for the linear map
> of that, but won't unmap the 4K when removing.
> 
> Likewise for the next level up, with s/2M/1G/ and s/4K/2M/.
> 

You're right. The confusion comes from the fact that we were only
considering the case where we are hot-removing memory that was previously
hot-added. In that case, the memory granularity of hot-add and hot-remove
is the same and fixed at compile time to get_memory_block_size() ==
1<<SECTION_SIZE_BITS (1GB by default).

In case we are removing memory that was in the linear mapping since boot,
then we might get in the situation you are describing above if someone has
manually changed SECTION_SIZE_BITS to a different value in sparsemem.h.
If not, I believe that the fact that we are removing one aligned GB per
shot should guarantee that we never have to split a PUD.

However, we should definitely handle all the cases. Since splitting a
P[UM]D might be a nightmare (if possible@all), two more or less clean
solutions I can think of are:
1. Only allow to hot-remove memory that was previously hot-added or,
2. Detect when we are trying to only partially remove a section mapped
   area of the linear mapping and just fail (and roll back) the remove
   process.

I think I prefer no. 2; it has the advantage of not forbidding a-priori
to remove non-hot-added memory; and its only disadvantage is that in some
(uncommon?) cases you would be just forbidden to hot-remove some memory,
with no distructive side effects.  What do you think?

[...]
 
> Thanks,
> Mark.
> 

Thanks again and best regards,
Andrea

^ permalink raw reply

* [PATCH v2 3/5] MAINTAINERS: add Atmel PTC entries
From: Ludovic Desroches @ 2017-04-21  9:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170421083924.15096-4-ludovic.desroches@microchip.com>

On Fri, Apr 21, 2017 at 10:39:22AM +0200, Ludovic Desroches wrote:
> Add entries for the Atmel PTC Subsystem.
> 
> Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com>
> ---
>  MAINTAINERS | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 860dacb..e1b8b0e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2323,6 +2323,13 @@ F:	Documentation/devicetree/bindings/input/atmel,maxtouch.txt
>  F:	drivers/input/touchscreen/atmel_mxt_ts.c
>  F:	include/linux/platform_data/atmel_mxt_ts.h
>  
> +ATMEL PTC SUBSYSTEM DRIVER
> +M:	Ludovic Desroches <ludovic.desroches@microchip.com>
> +L:	linux-input at vger.kernel.org
> +S:	Supported
> +F:	drivers/input/misc/atmel_ptc.c
> +F:	include/uapi/linux/atmel_ptc.h

I'll remove it in v3.

> +
>  ATTO EXPRESSSAS SAS/SATA RAID SCSI DRIVER
>  M:	Bradley Grove <linuxdrivers@attotech.com>
>  L:	linux-scsi at vger.kernel.org
> -- 
> 2.9.0
> 

^ permalink raw reply

* [arm64] OOPS when using /proc/kcore to disassemble the kernel symbols in "perf top"
From: Mark Rutland @ 2017-04-21  9:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <58F99CC3.9050507@huawei.com>

Hi,

On Fri, Apr 21, 2017 at 01:46:43PM +0800, Tan Xiaojun wrote:
> On 2017/4/20 9:38, Tan Xiaojun wrote:
> > On 2017/4/19 19:49, Mark Rutland wrote:
> >> Hi,
> >>
> >> Ard, this sseems to be a nomap issue. Please see below.
> >>
> >> Xiaojun, for some reason, the first message in this thread didn't seem
> >> to make it to LAKML (or to me). In future could you please Cc me for
> >> emails regarding perf on arm/arm64?
> >>
> > 
> > Sorry, this is my negligence.
> > 
> >> On Wed, Apr 19, 2017 at 09:44:56AM +0530, Pratyush Anand wrote:
> >>> On Saturday 15 April 2017 02:18 PM, Tan Xiaojun wrote:
> >>>> My test server is Hisilicon D03/D05 (arm64).
> >>>> Kernel source code is 4.11-rc6 (up to date) and config (as an attachment in the end) is generated by defconfig.
> >>>> (Old version does not seem to have this problem. Linux-4.1 is fine and other versions I have not tested yet.)
> >>>
> >>> I tested with mustang(ARM64) and 4.11-rc6 and could not reproduce it.
> >>>
> 
> Hi, 
> Pratyush,
> 
> Sorry, could you test it again? Because I tested it many times and found it is not triggered every time.
> And you can run "perf top -U" and try more kernel symbols to increase the probability of occurrence, or 
> maybe you can try Mark's way "cat /proc/kcore > /dev/null".
> 
> I would like to confirm whether this is hardware related, but I have no other arm64 boards except the
> boards of Hisilicon.

As I mentioned in my prior reply, this is a bug in the way we handle
nomap memory in the kernel.

This is not hardware related, and this is not specific to perf.

The kcore code expects that if a vmalloc mapping has a corresponding
struct page, that it can be accessed via the linear mapping. However,
this is not true for nomap memory.

> I found the patch which introduced the problem.
> The commit is:
> 
> commit f9040773b7bbbd9e98eb6184a263512a7cfc133f
> Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Date:   Tue Feb 16 13:52:40 2016 +0100
> 
>     arm64: move kernel image to base of vmalloc area
> 
>     This moves the module area to right before the vmalloc area, and moves
>     the kernel image to the base of the vmalloc area. This is an intermediate
>     step towards implementing KASLR, which allows the kernel image to be
>     located anywhere in the vmalloc area.
> 
>     Since other subsystems such as hibernate may still need to refer to the
>     kernel text or data segments via their linears addresses, both are mapped
>     in the linear region as well. The linear alias of the text region is
>     mapped read-only/non-executable to prevent inadvertent modification or
>     execution.
> 
>     Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>     Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
> 
> It can work well without this patch in Linux-4.5-rc4. And it can
> trigger an OOPS with this patch in Linux-4.5-rc4.
> 
> I try to revert it in v4.11-rc6, but it involves too much conflict.
> So I need to understand this patch fist. Then I can known where the problem is.

Reverting this patch is not the correct fix.

The fix will either be changing the way we set things up for nomap
memory, or with additions to the kcore or vread code to cater for nomap.

Thanks,
Mark.

^ permalink raw reply

* linux-next: manual merge of the pm tree with the arm-soc tree
From: Arnd Bergmann @ 2017-04-21  9:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4b58d5ab-c924-97af-728f-b9f524e62a53@oracle.com>

On Fri, Apr 21, 2017 at 8:39 AM, santosh.shilimkar at oracle.com
<santosh.shilimkar@oracle.com> wrote:
> On 4/20/17 10:53 PM, Arnd Bergmann wrote:
>>
>> On Fri, Apr 21, 2017 at 2:54 AM, Stephen Rothwell <sfr@canb.auug.org.au>
>> wrote:
>>>
>>> Hi all,
>>>
>>> Today's linux-next merge of the pm tree got a conflict in:
>>>
>>>   include/dt-bindings/genpd/k2g.h
>>>
>>> between commit:
>>>
>>>   7cc119f29b19 ("dt-bindings: Add TI SCI PM Domains")
>>>
>>> from the arm-soc tree and commit:
>>>
>>>   45da8edd1741 ("dt-bindings: Add TI SCI PM Domains")
>>>
>>> from the pm tree.
>>>
>>> I fixed it up (I just used the pm tree version) and can carry the fix as
>>> necessary. This is now fixed as far as linux-next is concerned, but any
>>> non trivial conflicts should be mentioned to your upstream maintainer
>>> when your tree is submitted for merging.  You may also want to consider
>>> cooperating with the maintainer of the conflicting tree to minimise any
>>> particularly complex conflicts.
>>
>>
>> Dave, Santosh,
>>
>> any idea what happened here? It seems that we picked up the wrong
>> version of the tree, do we need to drop this from arm-soc?
>>
> Nope. Its because this series was in my 'next' branch for a week or
> so and now it made it via arm-soc tree next as well.
>
> I just cleaned up my next head so it linux-next next tag should have
> only arm-soc copy.

I still see two conflicting trees in linux-next as of today, neither of
them is your keystone tree:

arm-soc/next/drivers:
ae3874cc931b ARM: keystone: Drop PM domain support for k2g
52835d59fc6c soc: ti: Add ti_sci_pm_domains driver
7cc119f29b19 dt-bindings: Add TI SCI PM Domains
213ec7fed302 PM / Domains: Do not check if simple providers have phandle cells
a5ea7a0fcbd7 PM / Domains: Add generic data pointer to genpd data struct

pm/pm-domains:
9da73c55f95f ARM: keystone: Drop PM domain support for k2g
112572283742 soc: ti: Add ti_sci_pm_domains driver
45da8edd1741 dt-bindings: Add TI SCI PM Domains
b1013fa55589 PM / Domains: Do not check if simple providers have phandle cells
7030fc004df9 PM / Domains: Add generic data pointer to genpd data struct
b539cc82d493 PM / Domains: Ignore domain-idle-states that are not compatible
075c37d59ecd PM / Domains: Don't warn about IRQ safe device for an
always on PM domain
1c14967c6ea0 PM / Domains: Respect errors from genpd's ->power_off() callback
ffaa42e8a40b PM / Domains: Enable users of genpd to specify always on PM domains
41e2c8e0060d PM / Domains: Clean up code validating genpd's status
8ce95844c853 PM / Domain: remove conditional from error case

For all I can tell (and matching what Stephen found), the version in
the 'pm' tree
is the one you intended to send, while the version we merged into arm-soc
has not only a different git commit ID but also some odd comments that
are not present in the PM version:

+/*
+ * TI SCI Generic Power Domain Driver
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated -
https://urldefense.proofpoint.com/v2/url?u=http-3A__www.ti.com_&d=DwIBAg&c=RoP1YumCXCgaWHvlZYR8PQcxBKCX5YTpkKY057SbK10&r=XBn1JQGPwR8CsE7xpP3wPlG6DQU7qw8ym65xieNZ4hY&m=R6qGiR9DbG1C3EF_0mL-m-qkmSO64GklbFWpUzqt8fY&s=YTWcQCWi5lnIf4XHDLq1XDd4JbZv9xpqOwdPD8xEdZE&e=

      Arnd

^ permalink raw reply

* [PATCH v2 2/3] crypto: inside-secure: add SafeXcel EIP197 crypto engine driver
From: Antoine Tenart @ 2017-04-21  9:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170421073056.GA2041@Red>

Hi Corentin,

On Fri, Apr 21, 2017 at 09:30:56AM +0200, Corentin Labbe wrote:
> 
> I have some minor comment below

[?]

> > +	/*
> > +	 * Result Descriptor Ring prepare
> > +	 */
> 
> This is not preferred comment format for one line

Sure.

> 
> [...]
> 
> > +static int safexcel_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct resource *res;
> > +	struct safexcel_crypto_priv *priv;
> > +	int i, ret;
> > +
> > +	priv = devm_kzalloc(dev, sizeof(struct safexcel_crypto_priv),
> > +			    GFP_KERNEL);
> 
> sizeof(priv) is preferred as asked by checkpatch
> 
> [...]
> > +		ring_irq = devm_kzalloc(dev, sizeof(struct safexcel_ring_irq_data),
> > +					GFP_KERNEL);
> 
> same comment here

Sure.

> [...]
> > +#define EIP197_ALG_ARC4				BIT(7)
> > +#define EIP197_ALG_AES_ECB			BIT(8)
> > +#define EIP197_ALG_AES_CBC			BIT(9)
> > +#define EIP197_ALG_AES_CTR_ICM			BIT(10)
> > +#define EIP197_ALG_AES_OFB			BIT(11)
> > +#define EIP197_ALG_AES_CFB			BIT(12)
> > +#define EIP197_ALG_DES_ECB			BIT(13)
> > +#define EIP197_ALG_DES_CBC			BIT(14)
> > +#define EIP197_ALG_DES_OFB			BIT(16)
> > +#define EIP197_ALG_DES_CFB			BIT(17)
> > +#define EIP197_ALG_3DES_ECB			BIT(18)
> > +#define EIP197_ALG_3DES_CBC			BIT(19)
> > +#define EIP197_ALG_3DES_OFB			BIT(21)
> > +#define EIP197_ALG_3DES_CFB			BIT(22)
> > +#define EIP197_ALG_MD5				BIT(24)
> > +#define EIP197_ALG_HMAC_MD5			BIT(25)
> 
> Does MD5, DES and 3DES will be added later ?

They might be added yes. And as these bits describe a register used to
configure the engine it's nice to have a proper definition of what all
combinations do.

> [...]
> > +static const u8 sha1_zero_digest[SHA1_DIGEST_SIZE] = {
> > +	0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
> > +	0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
> > +};
> > +
> > +static const u8 sha224_zero_digest[SHA224_DIGEST_SIZE] = {
> > +	0xd1, 0x4a, 0x02, 0x8c, 0x2a, 0x3a, 0x2b, 0xc9, 0x47, 0x61,
> > +	0x02, 0xbb, 0x28, 0x82, 0x34, 0xc4, 0x15, 0xa2, 0xb0, 0x1f,
> > +	0x82, 0x8e, 0xa6, 0x2a, 0xc5, 0xb3, 0xe4, 0x2f
> > +};
> > +
> > +static const u8 sha256_zero_digest[SHA256_DIGEST_SIZE] = {
> > +	0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb,
> > +	0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4,
> > +	0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52,
> > +	0xb8, 0x55
> > +};
> 
> Thoses structures are already defined in crypto (sha1_zero_message_hash, etc...)
> You can use it since you select SHAxxx in Kconfig

That's right, I'll use these definitions instead.

> [...]
> > +static int safexcel_hmac_init_pad(struct ahash_request *areq,
> > +				  unsigned int blocksize, const u8 *key,
> > +				  unsigned int keylen, u8 *ipad, u8 *opad)
> > +{
> > +	struct safexcel_ahash_result result;
> > +	struct scatterlist sg;
> > +	int ret, i;
> > +	u8 *keydup;
> > +
> > +	if (keylen <= blocksize) {
> > +		memcpy(ipad, key, keylen);
> > +	} else {
> > +		keydup = kmemdup(key, keylen, GFP_KERNEL);
> > +		if (!keydup)
> > +			return -ENOMEM;
> > +
> > +		ahash_request_set_callback(areq, CRYPTO_TFM_REQ_MAY_BACKLOG,
> > +					   safexcel_ahash_complete, &result);
> > +		sg_init_one(&sg, keydup, keylen);
> > +		ahash_request_set_crypt(areq, &sg, ipad, keylen);
> > +		init_completion(&result.completion);
> > +
> > +		ret = crypto_ahash_digest(areq);
> > +		if (ret == -EINPROGRESS) {
> > +			wait_for_completion_interruptible(&result.completion);
> > +			ret = result.error;
> > +		}
> > +
> > +		/* Avoid leaking */
> > +		memset(keydup, 0, keylen);
> 
> It is safer to use memzero_explicit

Good to know, I'll update.

> > +		kfree(keydup);
> > +
> > +		if (ret)
> > +			return ret;
> > +
> > +		keylen = crypto_ahash_digestsize(crypto_ahash_reqtfm(areq));
> > +	}
> > +
> > +	memset(ipad + keylen, 0, blocksize - keylen);
> > +	memcpy(opad, ipad, blocksize);
> > +
> > +	for (i = 0; i < blocksize; i++) {
> > +		ipad[i] ^= 0x36;
> > +		opad[i] ^= 0x5c;
> 
> What are these constant ?

They are defined in the HMAC RFC, as ipad and opad values. See
https://www.ietf.org/rfc/rfc2104.txt.

> [...]
> > +static int safexcel_hmac_sha1_setkey(struct crypto_ahash *tfm, const u8 *key,
> > +				     unsigned int keylen)
> > +{
> > +	struct safexcel_ahash_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm));
> > +	struct safexcel_ahash_export_state istate, ostate;
> > +	int ret, i;
> > +
> > +	ret = safexcel_hmac_setkey("safexcel-sha1", key, keylen, &istate, &ostate);
> 
> Perhaps you could use the algname instead of "safexcel-sha1"

No we can't (as of now), because using the SHA implementation of this
driver for partial hashes require special treatments (which we do).

> > +	if (ret)
> > +		return ret;
> > +
> > +	memcpy(ctx->ipad, &istate.state, SHA1_DIGEST_SIZE);
> > +	memcpy(ctx->opad, &ostate.state, SHA1_DIGEST_SIZE);
> 
> Perhaps you could the digest_size from alg_template

Well, this HMAC setkey function is dedicated to SHA1 for other reasons
than only this digest size. So why bother retrieving something which we
know is already fixed?

> [...]
> > +struct safexcel_alg_template safexcel_alg_sha256 = {
> > +	.type = SAFEXCEL_ALG_TYPE_AHASH,
> > +	.alg.ahash = {
> > +		.init = safexcel_sha256_init,
> > +		.update = safexcel_ahash_update,
> > +		.final = safexcel_ahash_final,
> > +		.finup = safexcel_ahash_finup,
> > +		.digest = safexcel_sha256_digest,
> > +		.export = safexcel_ahash_export,
> > +		.import = safexcel_ahash_import,
> > +		.halg = {
> > +			.digestsize = SHA256_DIGEST_SIZE,
> > +			.statesize = sizeof(struct safexcel_ahash_export_state),
> > +			.base = {
> > +				.cra_name = "sha256",
> > +				.cra_driver_name = "safexcel-sha256",
> > +				.cra_priority = 300,
> > +				.cra_flags = CRYPTO_ALG_ASYNC |
> > +					     CRYPTO_ALG_KERN_DRIVER_ONLY,
> 
> Why do use CRYPTO_ALG_KERN_DRIVER_ONLY ?

See http://lxr.free-electrons.com/source/include/linux/crypto.h#L97.

Thanks,
Antoine

-- 
Antoine T?nart, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170421/11564af3/attachment-0001.sig>

^ permalink raw reply

* [PATCH 3/3] bus: fsl-mc: dpio: change CENA regs to be cacheable
From: Mark Rutland @ 2017-04-21  9:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1492716858-24509-4-git-send-email-Haiying.Wang@nxp.com>

On Thu, Apr 20, 2017 at 03:34:18PM -0400, Haiying Wang wrote:
> plus non-shareable to meet the performance requirement.
> QMan's CENA region contains registers and structures that
> are 64byte in size and are inteneded to be accessed using a
> single 64 byte bus transaction, therefore this portal
> memory should be configured as cache-enabled. Also because
> the write allocate stash transcations of QBMan should be
> issued as cachable and non-coherent(non-sharable), we
> need to configure this region to be non-shareable.
> 
> Signed-off-by: Haiying Wang <Haiying.Wang@nxp.com>
> ---
>  drivers/staging/fsl-mc/bus/dpio/dpio-driver.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/staging/fsl-mc/bus/dpio/dpio-driver.c b/drivers/staging/fsl-mc/bus/dpio/dpio-driver.c
> index e36da20..97f909c 100644
> --- a/drivers/staging/fsl-mc/bus/dpio/dpio-driver.c
> +++ b/drivers/staging/fsl-mc/bus/dpio/dpio-driver.c
> @@ -168,10 +168,10 @@ static int dpaa2_dpio_probe(struct fsl_mc_device *dpio_dev)
>  	desc.cpu = next_cpu;
>  
>  	/*
> -	 * Set the CENA regs to be the cache inhibited area of the portal to
> -	 * avoid coherency issues if a user migrates to another core.
> +	 * Set the CENA regs to be the cache enalbed area of the portal to
> +	 * archieve the best performance.

Is migrating to another core no longer a concern?

As with my prior comments regarding the Non-Shareable mapping, I do not
thing this makes sense.

Thanks,
Mark.

>  	 */
> -	desc.regs_cena = ioremap_wc(dpio_dev->regions[1].start,
> +	desc.regs_cena = ioremap_cache_ns(dpio_dev->regions[1].start,
>  		resource_size(&dpio_dev->regions[1]));
>  	desc.regs_cinh = ioremap(dpio_dev->regions[1].start,
>  		resource_size(&dpio_dev->regions[1]));
> -- 
> 2.7.4
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH 2/3] bus: fsl-mc: dpio: enable qbman CENA portal memory access
From: Mark Rutland @ 2017-04-21  9:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1492716858-24509-3-git-send-email-Haiying.Wang@nxp.com>

Hi,

On Thu, Apr 20, 2017 at 03:34:17PM -0400, Haiying Wang wrote:
> Once we enable the cacheable portal memory, we need to do
> cache flush for enqueue, vdq, buffer release, and management
> commands, as well as invalidate and prefetch for the valid bit
> of management command response and next index of dqrr.
> 
> Signed-off-by: Haiying Wang <Haiying.Wang@nxp.com>
> ---
>  drivers/staging/fsl-mc/bus/dpio/qbman-portal.c | 23 +++++++++++++++++------
>  1 file changed, 17 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/staging/fsl-mc/bus/dpio/qbman-portal.c b/drivers/staging/fsl-mc/bus/dpio/qbman-portal.c
> index 2a3ea29..e16121c 100644
> --- a/drivers/staging/fsl-mc/bus/dpio/qbman-portal.c
> +++ b/drivers/staging/fsl-mc/bus/dpio/qbman-portal.c
> @@ -99,6 +99,14 @@ enum qbman_sdqcr_fc {
>  	qbman_sdqcr_fc_up_to_3 = 1
>  };
>  
> +#define dccvac(p) { asm volatile("dc cvac, %0;" : : "r" (p) : "memory"); }
> +#define dcivac(p) { asm volatile("dc ivac, %0" : : "r"(p) : "memory"); }

Please don't place arm64-specific assembly in drivers, and use the APIs
the architecture provides. We have a suite of cache maintenance APIs,
and abstractions atop of those that make them easier to deal with.

This patch pushes the code further from being acceptable outside of
staging.


> +static inline void qbman_inval_prefetch(struct qbman_swp *p, uint32_t offset)
> +{
> +	dcivac(p->addr_cena + offset);
> +	prefetch(p->addr_cena + offset);
> +}

It doesn't make sense to me to clean+invalidate an arbitrary amount of
data, then prefetch an arbitrary amount. What exactly is this code
trying to do?

If this is intended to make data visible to a non-coherent observer, a
clean would be sufficient.

If this is intended to make data from a non-coherent master visible to
the CPUs, then an invalidate would be sufficient.

In either case, we *must* take the cache line size, and the size of the
data into account. If the same cache line is being modified by multiple
observers, this will corrupt the data in some cases.

Architecturally cache maintenance requires particular memory barriers,
which are missing here and/or in callers. At best, this works by chance.

The existing cache maintenance APIs handle this, and all you need to do
here is to ensure suitable alignment and padding. Please don't write
your own cache maintenance like this.

Thanks,
Mark.

> +
>  /* Portal Access */
>  
>  static inline u32 qbman_read_register(struct qbman_swp *p, u32 offset)
> @@ -189,7 +197,7 @@ struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d)
>  	p->addr_cinh = d->cinh_bar;
>  
>  	reg = qbman_set_swp_cfg(p->dqrr.dqrr_size,
> -				1, /* Writes Non-cacheable */
> +				0, /* Writes cacheable */
>  				0, /* EQCR_CI stashing threshold */
>  				3, /* RPM: Valid bit mode, RCR in array mode */
>  				2, /* DCM: Discrete consumption ack mode */
> @@ -315,6 +323,7 @@ void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, u8 cmd_verb)
>  
>  	dma_wmb();
>  	*v = cmd_verb | p->mc.valid_bit;
> +	dccvac(cmd);
>  }
>  
>  /*
> @@ -325,6 +334,7 @@ void *qbman_swp_mc_result(struct qbman_swp *p)
>  {
>  	u32 *ret, verb;
>  
> +	qbman_inval_prefetch(p, QBMAN_CENA_SWP_RR(p->mc.valid_bit));
>  	ret = qbman_get_cmd(p, QBMAN_CENA_SWP_RR(p->mc.valid_bit));
>  
>  	/* Remove the valid-bit - command completed if the rest is non-zero */
> @@ -435,6 +445,7 @@ int qbman_swp_enqueue(struct qbman_swp *s, const struct qbman_eq_desc *d,
>  	/* Set the verb byte, have to substitute in the valid-bit */
>  	dma_wmb();
>  	p->verb = d->verb | EQAR_VB(eqar);
> +	dccvac(p);
>  
>  	return 0;
>  }
> @@ -627,6 +638,7 @@ int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d)
>  	/* Set the verb byte, have to substitute in the valid-bit */
>  	p->verb = d->verb | s->vdq.valid_bit;
>  	s->vdq.valid_bit ^= QB_VALID_BIT;
> +	dccvac(p);
>  
>  	return 0;
>  }
> @@ -680,8 +692,7 @@ const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s)
>  				 s->dqrr.next_idx, pi);
>  			s->dqrr.reset_bug = 0;
>  		}
> -		prefetch(qbman_get_cmd(s,
> -				       QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)));
> +		qbman_inval_prefetch(s,	QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx));
>  	}
>  
>  	p = qbman_get_cmd(s, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx));
> @@ -696,8 +707,7 @@ const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s)
>  	 * knew from reading PI.
>  	 */
>  	if ((verb & QB_VALID_BIT) != s->dqrr.valid_bit) {
> -		prefetch(qbman_get_cmd(s,
> -				       QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)));
> +		qbman_inval_prefetch(s, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx));
>  		return NULL;
>  	}
>  	/*
> @@ -720,7 +730,7 @@ const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s)
>  	    (flags & DPAA2_DQ_STAT_EXPIRED))
>  		atomic_inc(&s->vdq.available);
>  
> -	prefetch(qbman_get_cmd(s, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)));
> +	qbman_inval_prefetch(s, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx));
>  
>  	return p;
>  }
> @@ -848,6 +858,7 @@ int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d,
>  	 */
>  	dma_wmb();
>  	p->verb = d->verb | RAR_VB(rar) | num_buffers;
> +	dccvac(p);
>  
>  	return 0;
>  }
> -- 
> 2.7.4
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH v5 1/4] gpio: mvebu: Add limited PWM support
From: Thomas Petazzoni @ 2017-04-21  9:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170412151932.GE7023@lunn.ch>

Hello,

On Wed, 12 Apr 2017 17:19:32 +0200, Andrew Lunn wrote:

> Yep. It was a compromise. By adding a new binding for the GPIO driver,
> this might be possible. But it did not seem worth such a major change.
> 
> The prime use of this feature is for controlling a fan. So far, i've
> not seen any hardware with more than one fan, i.e. needs more than one
> PWM. Nor have i seen any hardware with the GPIO for the fan being on
> the third bank. A hardware manufacture could add multiple fans, but i
> doubt it, they make noise and fail. And if a manufacture does place a
> fan on the third bank, it can still be controlled as a plain GPIO fan,
> as we have been doing for the last few years.

Right.

> So i personally think it is an O.K. compromise.

I clearly don't want to block this, but I believe this is a very good
illustration of why stable DT bindings simply don't work. We are
realizing here that having each GPIO bank represented as a separate DT
node doesn't work, because this blinking functionality is not per GPIO
bank, but global to all GPIO banks.

I am totally fine with compromise, and having things simple first, and
extend them later if needed. But this stable DT binding rule makes this
quite impossible: what is a compromise today might put you in big
troubles tomorrow.

Anyway, it's fine for me, I don't think it's worth the effort making a
much more complicated solution/change.

Best regards,

Thomas
-- 
Thomas Petazzoni, CTO, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* [PATCH v4 01/22] KVM: arm/arm64: Add vITS save/restore API documentation
From: Christoffer Dall @ 2017-04-21  9:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <e60aa03e-b6af-3be3-6afd-ae795c45cf33@redhat.com>

On Mon, Apr 10, 2017 at 04:26:14PM +0200, Auger Eric wrote:
> Hi Christoffer,
> 
> On 08/04/2017 20:17, Christoffer Dall wrote:
> > Hi Eric,
> > 
> > Most of my comments below are just me being picky about text when
> > defining a user space ABI, so I think this mainly looks good, but just
> > needs a bit of clarify, except the versioning aspect and exporting the
> > pending table vie the redistributor instead, as Marc and Andre have
> > pointed out.
> > 
> > On Mon, Mar 27, 2017 at 11:30:51AM +0200, Eric Auger wrote:
> >> Add description for how to access vITS registers and how to flush/restore
> >> vITS tables into/from memory
> >>
> >> Signed-off-by: Eric Auger <eric.auger@redhat.com>
> >>
> >> ---
> >> v3 -> v4:
> >> - take into account Peter's comments:
> >>   - typos
> >>   - KVM_DEV_ARM_VGIC_GRP_ITS_TABLES kvm_device_attr = 0
> >>   - add a validity bit in DTE
> >>   - document all fields in CTE and ITE
> >>   - document ABI revision
> >> - take into account Andre's comments:
> >>   - document restrictions about GITS_CREADR writing and GITS_IIDR
> >>   - document -EBUSY error if one or more VCPUS are runnning
> >>   - document 64b registers only can be accessed with 64b access
> >> - itt_addr field matches bits [51:8] of the itt_addr
> >>
> >> v1 -> v2:
> >> - DTE and ITE now are 8 bytes
> >> - DTE and ITE now indexed by deviceid/eventid
> >> - use ITE name instead of ITTE
> >> - mentions ITT_addr matches bits [51:8] of the actual address
> >> - mentions LE layout
> >> ---
> >>  Documentation/virtual/kvm/devices/arm-vgic-its.txt | 118 +++++++++++++++++++++
> >>  1 file changed, 118 insertions(+)
> >>
> >> diff --git a/Documentation/virtual/kvm/devices/arm-vgic-its.txt b/Documentation/virtual/kvm/devices/arm-vgic-its.txt
> >> index 6081a5b..0902d20 100644
> >> --- a/Documentation/virtual/kvm/devices/arm-vgic-its.txt
> >> +++ b/Documentation/virtual/kvm/devices/arm-vgic-its.txt
> >> @@ -36,3 +36,121 @@ Groups:
> >>      -ENXIO:  ITS not properly configured as required prior to setting
> >>               this attribute
> >>      -ENOMEM: Memory shortage when allocating ITS internal data
> >> +
> >> +  KVM_DEV_ARM_VGIC_GRP_ITS_REGS
> >> +  Attributes:
> >> +      The attr field of kvm_device_attr encodes the offset of the
> >> +      ITS register, relative to the ITS control frame base address
> >> +      (ITS_base).
> >> +
> >> +      kvm_device_attr.addr points to a __u64 value whatever the width
> >> +      of the addressed register (32/64 bits). 64 bit registers can only
> >> +      be accessed with full length.
> >> +
> >> +      Writes to read-only registers are ignored by the kernel except for:
> >> +      - GITS_READR. It needs to be restored otherwise commands in the queue
> >> +        will be re-executed after CWRITER setting. Writing this register is
> >                                      ^^^^^^^^^^^^^^^
> > 
> > "after restoring CWRITER." ?
> OK
> > 
> >> +        allowed if the ITS is not enabled (GITS_CTLR.enable = 0). Also it
> > 
> > Does that mean that you can only save/restore a disabled ITS or does it
> > mean that initially after creating the ITS it is disabled and userspace
> > should restore the CWRITER before restoring GITS_CTLR (which may enable
> > the ITS) ?
> No I meant the second. As normally the ITS is responsible for updating
> the GITS_READR, if the userspace plays with it while the ITS is enabled
> this can mess everything. So the userspace is supposed to restore the
> GITS_READR *before* restoring the GITS_CTLR which is likely to enable
> the ITS.
> > 
> >> +        needs to be restored after GITS_CBASER since a write to GITS_CBASER
> >> +        resets GITS_CREADR.
> >> +      - GITS_IIDR. Its Revision field encodes the table layout ABI revision.
> > 
> > How is this really going to work?  Your ABI here must be backwards
> > compatible for future revisions, so what is userspace supposed to do,
> > when it reads a newer revision than it was programmed for?
> destination ABI revision must be >= source ABI revision
> By restoring the IIDR value on the destination side, the userspace
> informs destination KVM about the layout of the table. If the
> destination KVM does not support this ABI revision, the restoration of
> the ITS table fails. Is that wrong?
> 

No, that's fine.  I think my confusion was that I didn't appreciate that
the revision thing is for the table layouts only, and not the other
calls here.

> > 
> > I think we need a more clear description of how the revision is going to
> > be used, such that each operation on the ITS here is described as
> > requiring a minimum revision X, and making sure that userspace can
> > safely ignore things that are of a higher revision number, while at the
> > same time userspace can decide not to use newer features with older
> > kernels.
> 
> At the moment I did not plan to implement any way for the userspace to
> force the ITS state save in a specified ABI revision. Today it uses the
> higher revision. Let's say that ABI V2 supports GICv4, save of source
> ITS with be done in v2 layout. A destination only supporting GICv3 (ABI
> v1) won't be able to read that format. Does it make sense?
> 

That makes sense, yes.

> > 
> >> +
> >> +      For other registers, getting or setting a register has the same
> >> +      effect as reading/writing the register on real hardware.
> >> +  Errors:
> >> +    -ENXIO: Offset does not correspond to any supported register
> >> +    -EFAULT: Invalid user pointer for attr->addr
> >> +    -EINVAL: Offset is not 64-bit aligned
> >> +    -EBUSY: one or more VCPUS are running
> >> +
> >> +  KVM_DEV_ARM_VGIC_GRP_ITS_TABLES
> > 
> > Shouldn't this be called KVM_DEV_ARM_VGIC_GRP_FLUSH_ITS_TABLES ?
> At the moment this group is used for flush and restore. But as you have
> the same remark as Andre I guess it is worth having separate commands in
> KVM_DEV_ARM_VGIC_GRP_CTRL then.
> > 
> >> +  Attributes
> >> +       The attr field of kvm_device_attr must be zero.
> >> +
> >> +       request the flush-save/restore of the ITS tables, namely
> > 
> > Nit: Request (upper case R)
> OK
> > 
> > what does flush-save/restore mean as opposed to just flush?
> meant flush/restore
> > 
> >> +       the device table, the collection table, all the ITT tables,
> >> +       the LPI pending tables. On save, the tables are flushed
> > 
> > , and the LPI pending table.
> OK
> > 
> >> +       into guest memory at the location provisioned by the guest
> >> +       in GITS_BASER (device and collection tables), in the MAPD
> >> +       command (ITT_addr), GICR_PENDBASERs (pending tables).
> >> +
> >> +       This means the GIC should be restored before the ITS and all
> > 
> > should or must?  Is this enforced?
> must. yes it is.
> > 
> >> +       ITS registers but the GITS_CTLR must be restored before
> >> +       restoring the ITS tables.
> >> +
> >> +       The GITS_READR and GITS_IIDR read-only registers must also
> >> +       be restored before the table restore. The IIDR revision field
> >> +       encodes the ABI revision of the table layout. If not set by
> >> +       user space, the restoration of the tables will fail.
> > 
> > consider rewording: ", restoring the ITS tables will fail."
> OK
> > 
> >> +
> >> +       Note the LPI configuration table is read-only for the
> > 
> > Note that
> OK
> > 
> >> +       in-kernel ITS and its save/restore goes through the standard
> > 
> > and saving/restoring it is done via the normal process to save/restore
> > guest RAM.
> OK
> > 
> >> +       RAM save/restore.
> >> +
> >> +       The layout of the tables in guest memory defines an ABI.
> >> +       The entries are laid in little endian format as follows;
> > 
> > s/;/:/
> OK
> > 
> > It's a bit weird to say "as follows:" and then proceed with the error
> > descriptions.  I would simply say "as described in the following
> > paragraph."
> OK
> > 
> >> +
> >> +  Errors:
> >> +    -EINVAL: kvm_device_attr not equal to 0, invalid table data
> >> +    -EFAULT: invalid guest ram access
> >> +    -EBUSY: one or more VCPUS are running
> >> +
> >> +    ITS Table ABI REV1:
> >> +    -------------------
> >> +
> >> +    The device table and ITE are respectively indexed by device id and
> > 
> > s/ITE/ITT/
> right
> > 
> > are indexed by the device id and eventid, respectively.
> > 
> >> +    eventid. The collection table however is not indexed by collection id:
> > 
> > ...by collection id, instead all the CTEs are written...
> > 
> >> +    CTE are written at the beginning of the buffer.
> > 
> > in any particular order, or?
> no order or creation

Probably good to point that out.

> > 
> >> +
> >> +    Device Table Entry (DTE) layout: entry size = 8 bytes
> >> +
> >> +    bits:     | 63| 62 ... 49 | 48 ... 5 | 4 ... 0 |
> >> +    values:   | V |   next    | ITT_addr |  Size   |
> >> +
> >> +    where;
> >> +    - V indicates whether the entry is valid,
> >> +    - ITT_addr matches bits [51:8] of the ITT address (256B aligned),
> >> +    - next field is meaningful only if the entry is valid.
> > 
> > is the ITT_addr meaningful if the entry is not valid?
> no, I will add this detail.
> > 
> >> +    It equals to 0 if this entry is the last one; otherwise it corresponds
> >> +    to the minimum between the offset to the next device id and 2^14 -1.
> > 
> > I don't easily understand this last paragraph and the indentation is
> > weird and makes it look like it's not explaining the next field.
> 
> reworded:
> It equals to 0 if this entry is the last one; otherwise it corresponds
> to the the deviceid offset to the next DTE, capped by 2^14 -1.
> > 
> > You're missing a description of the size field.  Size in what unit?
> > Size of what?
> right, added "size matches the MAPD Size semantic."
> > 
> > 
> >> +
> >> +    Collection Table Entry (CTE) layout: entry size = 8 bytes
> >> +
> >> +    bits:     | 63| 62 ..  52  | 51 ... 16 | 15  ...   0 |
> >> +    values:   | V |    RES0    |  RDBase   |    ICID     |
> >> +
> >> +    where:
> >> +    - V indicates whether the entry is valid,
> > 
> > Do we explain anywhere what RES0 means?
> Added RES0: reserved field with Should-Be-Zero-or-Preserved behavior.
> > 
> >> +    - RDBase matches the PE number (GICR_TYPER.Processor_Number),
> > 
> > is 'matches' the right verb to use here?
> is
> > 
> > What exactly is the format of GICR_TYPER.Processor_Number ?
> > 
> >> +    - ICID matches the collection ID
> > 
> > again, is 'matches' the right verb to use here?
> is
> > 
> >> +
> >> +    Interrupt Translation Entry (ITE) layout: entry size = 8 bytes
> >> +
> >> +    bits:     | 63 ... 48 | 47 ... 16 | 15 ... 0 |
> >> +    values:   |    next   |   pINTID  |  ICID    |
> >> +
> >> +    where:
> >> +    - pINTID is the physical LPI ID,
> >> +    - ICID is the collection ID,
> > 
> > here you use 'is' intead of 'matches'.  At leat be consistent.
> is
> > 
> >> +    - next field is meaningful only if the entry is valid (pINTID != 0).
> >> +    It equals to 0 if this entry is the last one; otherwise it corresponds
> >> +    to the minimum between the eventid offset to the next ITE and 2^16 -1.
> > 
> > same comments, as above.
> ok
> > 
> > also, can you list the field in the order they appear?
> ok
> > 
> >> +
> >> +    LPI Pending Table layout:
> >> +
> >> +    As specified in the ARM Generic Interrupt Controller Architecture
> >> +    Specification GIC Architecture version 3.0 and version 4. The first
> > 
> > "...version 4, the first".  ("As specified in X." is not a sentence).
> > 
> >> +    1kB is not modified and therefore should contain zeroes.
> > 
> > should or must?  or always contains zeros?  Will you return an error if
> > userspace puts something non-zero in there?
> no I don't. Spec says it should initialized to zero. $
> > 
> >> +
> >> +    Future evolutions of the ITS table layout:
> >> +
> >> +    At the moment the table layout is defined and optimized for physical
> >> +    LPI support.
> > 
> > This comment is a bit confusing, because this is all about virtual
> > interrupts really, so 'physical from the point of view of the VM', but I
> > think you should just drop this sentence.
> OK
> > 
> >> +
> >> +    In the future we might implement direct injection of virtual LPIS.
> > 
> > For nesting, yes?  (on the host this should not be visible here, should
> > it?)
> yes for nesting.
> > 
> >> +    This will require an upgrade of the table layout and an evolution of
> >> +    the ABI. The ABI revision is encoded in the GITS_IIDR revision field.
> >> +    That register must be restored before the table restoration, otherwise
> >> +    the operation will fail.
> > 
> > Hmm, I thought we dealt with that before, feels a bit out of place.
> 
> OK moved to the register description.
> > 
> >> +
> >> +    ABI V1: GITS_IIDR.Revision = 1
> > 
> > I feel like this should go in the ITS register description of the IIDR.
> > Most likely, I think this particular line can be dropped, but all other
> > definitions in this file can be annoted with a minimum revision number
> > ensuring that future revisions implement this, plus potentially more
> > stuff.
> I already put the following title, "ITS Table ABI REV1" for the table
> layout description. My intent was to create a new chapter per revision.
> Is it OK?
> 

I'll have a look at the next revision.

Thanks for responding to all my comments.

-Christoffer

^ permalink raw reply

* [PATCH 1/3] arm64: extend ioremap for cacheable non-shareable memory
From: Mark Rutland @ 2017-04-21  9:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1492716858-24509-2-git-send-email-Haiying.Wang@nxp.com>

Hi,

I notice you missed Catalin and Will from Cc. In future, please ensure
that you Cc them when altering arm64 arch code.

On Thu, Apr 20, 2017 at 03:34:16PM -0400, Haiying Wang wrote:
> NXP arm64 based SoC needs to allocate cacheable and
> non-shareable memory for the software portals of
> Queue manager, so we extend the arm64 ioremap support
> for this memory attribute.

NAK to this patch.

It is not possible to safely use Non-Shareable attributes in Linux page
tables, given that these page tables are shared by all PEs (i.e. CPUs).

My understanding is that if several PEs map a region as Non-Shareable,
the usual background behaviour of the PEs (e.g. speculation,
prefetching, natural eviction) mean that uniprocessor semantics are not
guaranteed (i.e. a read following a write may see stale data).

For example, in a system like:

    +------+  +------+
    | PE-a |  | PE-b |
    +------+  +------+
    | L1-a |  | L1-b |
    +------+  +------+
       ||        ||
    +----------------+
    |  Shared cache  |
    +----------------+
            ||
    +----------------+
    |     Memory     |
    +----------------+

... you could have a sequence like:

1) PE-a allocates a line into L1-a for address X in preparation for a
   store.

2) PE-b allocates a line into L1-b for the same address X as a result of
   speculation.

3) PE-a makes a store to the line in L1-a. Since address X is mapped as
   Non-shareable, no snoops are performed to keep other copies of the
   line in sync.

4) As a result of explicit maintenance or as a natural eviction, L1-a
   evicts its line into shared cache. The shared cache subsequently
   evicts this to memory.

5) L1-b evicts its line to shared cache as a natural eviction.

6) L1-a fetches the line from shared cache in response to a load by
   PE-a, returning stale data (i.e. the store is lost).

No amount of cache maintenance can avoid this. In general, Non-Shareable
mappings are a bad idea.

Thanks,
Mark.

> Signed-off-by: Haiying Wang <Haiying.Wang@nxp.com>
> ---
>  arch/arm64/include/asm/io.h           | 1 +
>  arch/arm64/include/asm/pgtable-prot.h | 1 +
>  2 files changed, 2 insertions(+)
> 
> diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
> index 0c00c87..b6f03e7 100644
> --- a/arch/arm64/include/asm/io.h
> +++ b/arch/arm64/include/asm/io.h
> @@ -170,6 +170,7 @@ extern void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size);
>  #define ioremap_nocache(addr, size)	__ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
>  #define ioremap_wc(addr, size)		__ioremap((addr), (size), __pgprot(PROT_NORMAL_NC))
>  #define ioremap_wt(addr, size)		__ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
> +#define ioremap_cache_ns(addr, size)	__ioremap((addr), (size), __pgprot(PROT_NORMAL_NS))
>  #define iounmap				__iounmap
>  
>  /*
> diff --git a/arch/arm64/include/asm/pgtable-prot.h b/arch/arm64/include/asm/pgtable-prot.h
> index 2142c77..7fc7910 100644
> --- a/arch/arm64/include/asm/pgtable-prot.h
> +++ b/arch/arm64/include/asm/pgtable-prot.h
> @@ -42,6 +42,7 @@
>  #define PROT_NORMAL_NC		(PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_NC))
>  #define PROT_NORMAL_WT		(PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_WT))
>  #define PROT_NORMAL		(PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL))
> +#define PROT_NORMAL_NS		(PTE_TYPE_PAGE | PTE_AF | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL))
>  
>  #define PROT_SECT_DEVICE_nGnRE	(PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE))
>  #define PROT_SECT_NORMAL	(PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL))
> -- 
> 2.7.4
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH] iommu: arm-smmu: correct sid to mask
From: Peng Fan @ 2017-04-21  9:03 UTC (permalink / raw)
  To: linux-arm-kernel

>From code "SMR mask 0x%x out of range for SMMU",
so, we need to use mask, not sid.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index b493c99..5a06de2 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1467,7 +1467,7 @@ static int arm_smmu_add_device(struct device *dev)
 		}
 		if (mask & ~smmu->smr_mask_mask) {
 			dev_err(dev, "SMR mask 0x%x out of range for SMMU (0x%x)\n",
-				sid, smmu->smr_mask_mask);
+				mask, smmu->smr_mask_mask);
 			goto out_free;
 		}
 	}
-- 
2.6.6

^ permalink raw reply related

* [PATCH v2] dmaengine: sun4i: fix invalid argument
From: Mason @ 2017-04-21  8:43 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170421082417.7cqtevwuhjvfgjcx@lukather>

From: Marc Gonzalez <marc_gonzalez@sigmadesigns.com>

The "pchans_used" field is an unsigned long array.

for_each_clear_bit_from() expects an unsigned long pointer,
not an array address.

$ make C=2 drivers/dma/sun4i-dma.o
  CHECK   drivers/dma/sun4i-dma.c
drivers/dma/sun4i-dma.c:241:9: warning: incorrect type in argument 1 (different base types)
drivers/dma/sun4i-dma.c:241:9:    expected unsigned long const *p
drivers/dma/sun4i-dma.c:241:9:    got unsigned long ( *<noident> )[1]

Signed-off-by: Marc Gonzalez <marc_gonzalez@sigmadesigns.com>
---
 drivers/dma/sun4i-dma.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/dma/sun4i-dma.c b/drivers/dma/sun4i-dma.c
index 57aa227bfadb..f4ed3f17607c 100644
--- a/drivers/dma/sun4i-dma.c
+++ b/drivers/dma/sun4i-dma.c
@@ -238,7 +238,7 @@ static struct sun4i_dma_pchan *find_and_use_pchan(struct sun4i_dma_dev *priv,
 	}
 
 	spin_lock_irqsave(&priv->lock, flags);
-	for_each_clear_bit_from(i, &priv->pchans_used, max) {
+	for_each_clear_bit_from(i, priv->pchans_used, max) {
 		pchan = &pchans[i];
 		pchan->vchan = vchan;
 		set_bit(i, priv->pchans_used);
-- 
3.14159

^ permalink raw reply related

* [PATCH v2 5/5] ARM: at91/defconfig: add PTC driver to sama5_defconfig
From: Ludovic Desroches @ 2017-04-21  8:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170421083924.15096-1-ludovic.desroches@microchip.com>

Add Peripheral Touch Controller driver to sama5_defconfig.

Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com>
---
 arch/arm/configs/sama5_defconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/configs/sama5_defconfig b/arch/arm/configs/sama5_defconfig
index 777c9e9..7480665 100644
--- a/arch/arm/configs/sama5_defconfig
+++ b/arch/arm/configs/sama5_defconfig
@@ -116,6 +116,8 @@ CONFIG_KEYBOARD_GPIO=y
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_TOUCHSCREEN=y
 CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_ATMEL_PTC=m
 # CONFIG_SERIO is not set
 CONFIG_LEGACY_PTY_COUNT=4
 CONFIG_SERIAL_ATMEL=y
-- 
2.9.0

^ permalink raw reply related

* [PATCH v2 4/5] ARM: dts: at91: sama5d2: add PTC subsystem device
From: Ludovic Desroches @ 2017-04-21  8:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170421083924.15096-1-ludovic.desroches@microchip.com>

Add the Atmel Peripheral Touch Controller subsystem.

Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com>
---
 arch/arm/boot/dts/sama5d2.dtsi | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index 8067c71..fa4ed56 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -762,6 +762,12 @@
 						atmel,clk-output-range = <0 83000000>;
 					};
 
+					ptc_clk: ptc_clk {
+						#clock-cells = <0>;
+						reg = <58>;
+						atmel,clk-output-range = <0 83000000>;
+					};
+
 					classd_clk: classd_clk {
 						#clock-cells = <0>;
 						reg = <59>;
@@ -1310,6 +1316,16 @@
 				reg = <0xfc05c000 0x20>;
 			};
 
+			ptc at fc060000 {
+				compatible = "atmel,sama5d2-ptc";
+				reg = <0x00800000 0x10000
+				       0xfc060000 0xcf>;
+				interrupts = <58 IRQ_TYPE_LEVEL_HIGH 7>;
+				clocks = <&ptc_clk>, <&main>, <&clk32k>;
+				clock-names = "ptc_clk", "ptc_int_osc", "slow_clk";
+				status = "disabled";
+			};
+
 			chipid at fc069000 {
 				compatible = "atmel,sama5d2-chipid";
 				reg = <0xfc069000 0x8>;
-- 
2.9.0

^ permalink raw reply related

* [PATCH v2 3/5] MAINTAINERS: add Atmel PTC entries
From: Ludovic Desroches @ 2017-04-21  8:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170421083924.15096-1-ludovic.desroches@microchip.com>

Add entries for the Atmel PTC Subsystem.

Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 860dacb..e1b8b0e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2323,6 +2323,13 @@ F:	Documentation/devicetree/bindings/input/atmel,maxtouch.txt
 F:	drivers/input/touchscreen/atmel_mxt_ts.c
 F:	include/linux/platform_data/atmel_mxt_ts.h
 
+ATMEL PTC SUBSYSTEM DRIVER
+M:	Ludovic Desroches <ludovic.desroches@microchip.com>
+L:	linux-input at vger.kernel.org
+S:	Supported
+F:	drivers/input/misc/atmel_ptc.c
+F:	include/uapi/linux/atmel_ptc.h
+
 ATTO EXPRESSSAS SAS/SATA RAID SCSI DRIVER
 M:	Bradley Grove <linuxdrivers@attotech.com>
 L:	linux-scsi at vger.kernel.org
-- 
2.9.0

^ permalink raw reply related

* [PATCH v2 2/5] input: misc: introduce Atmel PTC driver
From: Ludovic Desroches @ 2017-04-21  8:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170421083924.15096-1-ludovic.desroches@microchip.com>

The Atmel Peripheral Touch Controller subsystem offers built-in hardware
for capacitive touch measurement on sensors that function as buttons,
sliders and wheels.

Two files are loaded when probing the driver:
- a firmware for the Pico Power Processor that computes raw data from
  the ADC front end to provide high level information as button touch or
  untouch, slider position, etc.
- a configuration file that describe the topology and the parameters of
  the sensors.

Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com>
---
 drivers/input/misc/Kconfig     |  12 +
 drivers/input/misc/Makefile    |   1 +
 drivers/input/misc/atmel_ptc.c | 723 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 736 insertions(+)
 create mode 100644 drivers/input/misc/atmel_ptc.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 79d0be9..5e27378 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -96,6 +96,18 @@ config INPUT_ATMEL_CAPTOUCH
 	  To compile this driver as a module, choose M here: the
 	  module will be called atmel_captouch.
 
+config INPUT_ATMEL_PTC
+	tristate "Atmel PTC Driver"
+	depends on OF || COMPILE_TEST
+	depends on SOC_SAMA5D2
+	help
+	  Say Y to enable support for the Atmel PTC Subsystem.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called atmel_ptc.
+	  If you compile it as a built-in driver, you have to build the
+	  firmware into the kernel or to use an initrd.
+
 config INPUT_BMA150
 	tristate "BMA150/SMB380 acceleration sensor support"
 	depends on I2C
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index b10523f..9470ec7 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_INPUT_ARIZONA_HAPTICS)	+= arizona-haptics.o
 obj-$(CONFIG_INPUT_ATI_REMOTE2)		+= ati_remote2.o
 obj-$(CONFIG_INPUT_ATLAS_BTNS)		+= atlas_btns.o
 obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH)	+= atmel_captouch.o
+obj-$(CONFIG_INPUT_ATMEL_PTC)		+= atmel_ptc.o
 obj-$(CONFIG_INPUT_BFIN_ROTARY)		+= bfin_rotary.o
 obj-$(CONFIG_INPUT_BMA150)		+= bma150.o
 obj-$(CONFIG_INPUT_CM109)		+= cm109.o
diff --git a/drivers/input/misc/atmel_ptc.c b/drivers/input/misc/atmel_ptc.c
new file mode 100644
index 0000000..612eaed
--- /dev/null
+++ b/drivers/input/misc/atmel_ptc.c
@@ -0,0 +1,723 @@
+/*
+ * Atmel PTC subsystem driver for SAMA5D2 devices and compatible.
+ *
+ * Copyright (C) 2017 Microchip,
+ *               2017 Ludovic Desroches <ludovic.desroches@microchip.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/cdev.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#define ATMEL_PTC_MAX_NODES	64
+#define ATMEL_PTC_MAX_SCROLLERS	4
+
+/* ----- PPP ----- */
+#define ATMEL_PPP_FIRMWARE_NAME	"atmel_ptc.bin"
+
+#define ATMEL_PPP_CONFIG	0x20
+#define ATMEL_PPP_CTRL		0x24
+#define ATMEL_PPP_CMD		0x28
+#define		ATMEL_PPP_CMD_STOP		0x1
+#define		ATMEL_PPP_CMD_RESET		0x2
+#define		ATMEL_PPP_CMD_RESTART		0x3
+#define		ATMEL_PPP_CMD_ABORT		0x4
+#define		ATMEL_PPP_CMD_RUN		0x5
+#define		ATMEL_PPP_CMD_RUN_LOCKED	0x6
+#define		ATMEL_PPP_CMD_RUN_OCD		0x7
+#define		ATMEL_PPP_CMD_UNLOCK		0x8
+#define		ATMEL_PPP_CMD_NMI		0x9
+#define		ATMEL_PPP_CMD_HOST_OCD_RESUME	0xB
+#define ATMEL_PPP_ISR		0x33
+#define		ATMEL_PPP_IRQ_MASK	GENMASK(7, 4)
+#define		ATMEL_PPP_IRQ0		BIT(4)
+#define		ATMEL_PPP_IRQ1		BIT(5)
+#define		ATMEL_PPP_IRQ2		BIT(6)
+#define		ATMEL_PPP_IRQ3		BIT(7)
+#define		ATMEL_PPP_NOTIFY_MASK	GENMASK(3, 0)
+#define		ATMEL_PPP_NOTIFY0	BIT(0)
+#define		ATMEL_PPP_NOTIFY1	BIT(1)
+#define		ATMEL_PPP_NOTIFY2	BIT(2)
+#define		ATMEL_PPP_NOTIFY3	BIT(3)
+#define ATMEL_PPP_IDR		0x34
+#define ATMEL_PPP_IER		0x35
+
+#define atmel_ppp_readb(ptc, reg)	readb_relaxed(ptc->ppp_regs + reg)
+#define atmel_ppp_writeb(ptc, reg, val)	writeb_relaxed(val, ptc->ppp_regs + reg)
+#define atmel_ppp_readl(ptc, reg)	readl_relaxed(ptc->ppp_regs + reg)
+#define atmel_ppp_writel(ptc, reg, val)	writel_relaxed(val, ptc->ppp_regs + reg)
+
+/* ----- QTM ----- */
+#define ATMEL_QTM_CONF_NAME		"atmel_ptc.conf"
+
+#define ATMEL_QTM_MB_OFFSET			0x4000
+#define ATMEL_QTM_MB_SIZE			0x1000
+
+#define ATMEL_QTM_MB_CMD_OFFSET			0x0
+#define		ATMEL_QTM_CMD_FIRM_VERSION		8
+#define		ATMEL_QTM_CMD_INIT			18
+#define		ATMEL_QTM_CMD_RUN			19
+#define		ATMEL_QTM_CMD_STOP			21
+#define		ATMEL_QTM_CMD_SET_ACQ_MODE_TIMER	24
+#define ATMEL_QTM_MB_NODE_GROUP_CONFIG_OFFSET	0x100
+#define ATMEL_QTM_MB_SCROLLER_CONFIG_OFFSET	0x81a
+#define		ATMEL_QTM_SCROLLER_TYPE_SLIDER		0x0
+#define		ATMEL_QTM_SCROLLER_TYPE_WHEEL		0x1
+#define ATMEL_QTM_MB_SCROLLER_DATA_OFFSET	0x842
+#define ATMEL_QTM_MB_TOUCH_EVENTS_OFFSET	0x880
+
+#define atmel_qtm_get_scroller_config(buf, id) \
+	memcpy(buf, \
+	       ptc->qtm_mb + ATMEL_QTM_MB_SCROLLER_CONFIG_OFFSET \
+	       + (id) * sizeof(struct atmel_qtm_scroller_config), \
+	       sizeof(struct atmel_qtm_scroller_config))
+
+#define atmel_qtm_get_scroller_data(buf, id) \
+	memcpy(buf, \
+	       ptc->qtm_mb + ATMEL_QTM_MB_SCROLLER_DATA_OFFSET \
+	       + (id) * sizeof(struct atmel_qtm_scroller_data), \
+	       sizeof(struct atmel_qtm_scroller_data))
+
+#define get_scroller_resolution(scroller_config) \
+	(1 << (scroller_config.resol_deadband >> 4))
+
+struct atmel_qtm_cmd {
+	u16	id;
+	u16	addr;
+	u32	data;
+} __packed;
+
+struct atmel_qtm_node_group_config {
+	u16	count;
+	u8	ptc_type;
+	u8	freq_option;
+	u8	calib_option;
+	u8	unused;
+} __packed;
+
+struct atmel_qtm_scroller_config {
+	u8	type;
+	u8	unused;
+	u16	key_start;
+	u8	key_count;
+	u8	resol_deadband;
+	u8	position_hysteresis;
+	u8	unused2;
+	u16	contact_min_threshold;
+} __packed;
+
+struct atmel_qtm_scroller_data {
+	u8	status;
+	u8	right_hyst;
+	u8	left_hyst;
+	u8	unused;
+	u16	raw_position;
+	u16	position;
+	u16	contact_size;
+} __packed;
+
+struct atmel_qtm_touch_events {
+	u32	key_event_id[2];
+	u32	key_enable_state[2];
+	u32	scroller_event_id;
+	u32	scroller_event_state;
+} __packed;
+
+struct atmel_ptc {
+	void __iomem		*ppp_regs;
+	void __iomem		*firmware;
+	int			irq;
+	u8			imr;
+	void __iomem		*qtm_mb;
+	struct clk		*clk_per;
+	struct clk		*clk_int_osc;
+	struct clk		*clk_slow;
+	struct device		*dev;
+	struct completion	ppp_ack;
+	unsigned int		button_keycode[ATMEL_PTC_MAX_NODES];
+	struct input_dev	*buttons_input;
+	struct input_dev	*scroller_input[ATMEL_PTC_MAX_SCROLLERS];
+	bool			buttons_registered;
+	bool			scroller_registered[ATMEL_PTC_MAX_SCROLLERS];
+	u32			button_event[ATMEL_PTC_MAX_NODES / 32];
+	u32			button_state[ATMEL_PTC_MAX_NODES / 32];
+	u32			scroller_event;
+	u32			scroller_state;
+};
+
+static void atmel_ppp_irq_enable(struct atmel_ptc *ptc, u8 mask)
+{
+	ptc->imr |= mask;
+	atmel_ppp_writeb(ptc, ATMEL_PPP_IER, mask & ATMEL_PPP_IRQ_MASK);
+}
+
+static void atmel_ppp_irq_disable(struct atmel_ptc *ptc, u8 mask)
+{
+	ptc->imr &= ~mask;
+	atmel_ppp_writeb(ptc, ATMEL_PPP_IDR, mask & ATMEL_PPP_IRQ_MASK);
+}
+
+static void atmel_ppp_notify(struct atmel_ptc *ptc, u8 mask)
+{
+	if (mask & ATMEL_PPP_NOTIFY_MASK) {
+		u8 notify = atmel_ppp_readb(ptc, ATMEL_PPP_ISR)
+			| (mask & ATMEL_PPP_NOTIFY_MASK);
+
+		atmel_ppp_writeb(ptc, ATMEL_PPP_ISR, notify);
+	}
+}
+
+static void atmel_ppp_irq_pending_clr(struct atmel_ptc *ptc, u8 mask)
+{
+	if (mask & ATMEL_PPP_IRQ_MASK) {
+		u8 irq = atmel_ppp_readb(ptc, ATMEL_PPP_ISR) & ~mask;
+
+		atmel_ppp_writeb(ptc, ATMEL_PPP_ISR, irq);
+	}
+}
+
+static void atmel_ppp_cmd_send(struct atmel_ptc *ptc, u32 cmd)
+{
+	atmel_ppp_writel(ptc, ATMEL_PPP_CMD, cmd);
+}
+
+static void atmel_ppp_irq_scroller_event(struct atmel_ptc *ptc)
+{
+	int i;
+
+	if (!ptc->scroller_event)
+		return;
+
+	for (i = 0; i < ATMEL_PTC_MAX_SCROLLERS; i++) {
+		u32 mask = 1 << i;
+		struct atmel_qtm_scroller_data scroller_data;
+		struct atmel_qtm_scroller_config scroller_config;
+
+		if (!(ptc->scroller_event & mask))
+			continue;
+
+		atmel_qtm_get_scroller_data(&scroller_data, i);
+		atmel_qtm_get_scroller_config(&scroller_config, i);
+
+		if (scroller_config.type == ATMEL_QTM_SCROLLER_TYPE_WHEEL)
+			input_report_abs(ptc->scroller_input[i],
+					 ABS_WHEEL, scroller_data.position);
+		else
+			input_report_abs(ptc->scroller_input[i],
+					 ABS_X, scroller_data.position);
+
+		input_report_key(ptc->scroller_input[i], BTN_TOUCH,
+				 scroller_data.status & 0x1);
+		input_sync(ptc->scroller_input[i]);
+	}
+}
+
+static void atmel_ppp_irq_button_event(struct atmel_ptc *ptc)
+{
+	int i, j;
+
+	for (i = 0; i < ATMEL_PTC_MAX_NODES / 32; i++) {
+		if (!ptc->button_event[i])
+			continue;
+
+		for (j = 0; j < 32; j++) {
+			u32 mask = 1 << j;
+			u32 state = ptc->button_state[i] & mask;
+			unsigned int key_id = i * 32 + j;
+
+			if (!(ptc->button_event[i] & mask))
+				continue;
+
+			input_report_key(ptc->buttons_input,
+					 ptc->button_keycode[key_id], !!state);
+			input_sync(ptc->buttons_input);
+		}
+	}
+}
+
+static void atmel_ppp_irq_touch_event(struct atmel_ptc *ptc)
+{
+	atmel_ppp_irq_scroller_event(ptc);
+	atmel_ppp_irq_button_event(ptc);
+}
+
+static irqreturn_t atmel_ppp_irq_handler(int irq, void *data)
+{
+	struct atmel_ptc *ptc = data;
+	u32 isr = atmel_ppp_readb(ptc, ATMEL_PPP_ISR) & ptc->imr;
+
+	/* QTM CMD acknowledgment */
+	if (isr & ATMEL_PPP_IRQ0) {
+		atmel_ppp_irq_disable(ptc, ATMEL_PPP_IRQ0);
+		atmel_ppp_irq_pending_clr(ptc, ATMEL_PPP_IRQ0);
+		complete(&ptc->ppp_ack);
+	}
+	/* QTM touch event */
+	if (isr & ATMEL_PPP_IRQ1) {
+		struct atmel_qtm_touch_events touch_events;
+		int i;
+
+		memcpy(&touch_events,
+		       ptc->qtm_mb + ATMEL_QTM_MB_TOUCH_EVENTS_OFFSET,
+		       sizeof(touch_events));
+
+		for (i = 0; i < ATMEL_PTC_MAX_NODES / 32; i++) {
+			ptc->button_event[i] = touch_events.key_event_id[i];
+			ptc->button_state[i] = touch_events.key_enable_state[i];
+		}
+		ptc->scroller_event = touch_events.scroller_event_id;
+		ptc->scroller_state = touch_events.scroller_event_state;
+
+		atmel_ppp_irq_pending_clr(ptc, ATMEL_PPP_IRQ1);
+
+		atmel_ppp_irq_touch_event(ptc);
+	}
+	/* Debug event */
+	if (isr & ATMEL_PPP_IRQ2)
+		atmel_ppp_irq_pending_clr(ptc, ATMEL_PPP_IRQ2);
+
+	return IRQ_HANDLED;
+}
+
+void atmel_qtm_cmd_send(struct atmel_ptc *ptc, struct atmel_qtm_cmd *cmd)
+{
+	int i, ret;
+
+	dev_dbg(ptc->dev, "%s: cmd=0x%x, addr=0x%x, data=0x%x\n",
+		__func__, cmd->id, cmd->addr, cmd->data);
+
+	memcpy(ptc->qtm_mb, cmd, sizeof(*cmd));
+
+	/* Once command performed, we'll get an IRQ. */
+	atmel_ppp_irq_enable(ptc, ATMEL_PPP_IRQ0);
+	/* Notify PPP that we have sent a command. */
+	atmel_ppp_notify(ptc, ATMEL_PPP_NOTIFY0);
+	/* Wait for IRQ from PPP. */
+	wait_for_completion(&ptc->ppp_ack);
+
+	/*
+	 * Register input devices only when QTM is started since we need some
+	 * information from the QTM configuration.
+	 */
+	if (cmd->id == ATMEL_QTM_CMD_RUN) {
+		if (ptc->buttons_input && !ptc->buttons_registered) {
+			ret = input_register_device(ptc->buttons_input);
+			if (ret)
+				dev_err(ptc->dev, "can't register input button device.\n");
+			else
+				ptc->buttons_registered = true;
+		}
+
+		for (i = 0; i < ATMEL_PTC_MAX_SCROLLERS; i++) {
+			struct input_dev *scroller = ptc->scroller_input[i];
+			struct atmel_qtm_scroller_config scroller_config;
+
+			if (!scroller || ptc->scroller_registered[i])
+				continue;
+
+			atmel_qtm_get_scroller_config(&scroller_config, i);
+
+			if (scroller_config.type ==
+			    ATMEL_QTM_SCROLLER_TYPE_SLIDER) {
+				unsigned int max = get_scroller_resolution(scroller_config);
+
+				input_set_abs_params(scroller, 0, 0, max, 0, 0);
+			}
+			ret = input_register_device(scroller);
+			if (ret)
+				dev_err(ptc->dev, "can't register input scroller device.\n");
+			else
+				ptc->scroller_registered[i] = true;
+		}
+	}
+
+	memcpy(cmd, ptc->qtm_mb, sizeof(*cmd));
+}
+
+static inline struct atmel_ptc *kobj_to_atmel_ptc(struct kobject *kobj)
+{
+	struct device *dev = kobj_to_dev(kobj);
+
+	return dev->driver_data;
+}
+
+static ssize_t atmel_qtm_mb_read(struct file *filp, struct kobject *kobj,
+				 struct bin_attribute *attr,
+				 char *buf, loff_t off, size_t count)
+{
+	struct atmel_ptc *ptc = kobj_to_atmel_ptc(kobj);
+	char *qtm_mb = (char *)ptc->qtm_mb;
+
+	dev_dbg(ptc->dev, "%s: off=0x%llx, count=%zu\n", __func__, off, count);
+
+	memcpy(buf, qtm_mb + off, count);
+
+	return count;
+}
+
+static ssize_t atmel_qtm_mb_write(struct file *filp, struct kobject *kobj,
+				  struct bin_attribute *attr,
+				  char *buf, loff_t off, size_t count)
+{
+	struct atmel_ptc *ptc = kobj_to_atmel_ptc(kobj);
+	char *qtm_mb = (char *)ptc->qtm_mb;
+
+	dev_dbg(ptc->dev, "%s: off=0x%llx, count=%zu\n", __func__, off, count);
+
+	if (off == 0 && count == sizeof(struct atmel_qtm_cmd))
+		atmel_qtm_cmd_send(ptc, (struct atmel_qtm_cmd *)buf);
+	else
+		memcpy(qtm_mb + off, buf, count);
+
+	return count;
+}
+
+static struct bin_attribute atmel_ptc_qtm_mb_attr = {
+	.attr = {
+		.name = "qtm_mb",
+		.mode = 0644,
+	},
+	.size = ATMEL_QTM_MB_SIZE,
+	.read = atmel_qtm_mb_read,
+	.write = atmel_qtm_mb_write,
+};
+
+/*
+ * From QTM MB configuration, we can't retrieve all the information needed
+ * to setup correctly input devices: buttons key codes and slider axis are
+ * missing.
+ */
+static int atmel_ptc_of_parse(struct atmel_ptc *ptc)
+{
+	struct device_node *sensor;
+	bool first_button = true;
+
+	/* Parse sensors. */
+	for_each_child_of_node(ptc->dev->of_node, sensor) {
+		if (!strcmp(sensor->name, "button")) {
+			u32 key_id, keycode;
+			struct input_dev *buttons = ptc->buttons_input;
+
+			if (of_property_read_u32(sensor, "reg", &key_id)) {
+				dev_err(ptc->dev, "reg is missing (%s)\n",
+					sensor->full_name);
+				return -EINVAL;
+			}
+
+			if (of_property_read_u32(sensor, "linux,keycode", &keycode)) {
+				dev_err(ptc->dev, "linux,keycode is missing (%s)\n",
+					sensor->full_name);
+				return -EINVAL;
+			}
+			ptc->button_keycode[key_id] = keycode;
+
+			/* All buttons are put together in a keyboard device. */
+			if (first_button) {
+				buttons = devm_input_allocate_device(ptc->dev);
+				if (!buttons)
+					return -ENOMEM;
+				buttons->name = "atmel_ptc_buttons";
+				buttons->dev.parent = ptc->dev;
+				buttons->keycode = ptc->button_keycode;
+				buttons->keycodesize = sizeof(ptc->button_keycode[0]);
+				buttons->keycodemax = ATMEL_PTC_MAX_NODES;
+				ptc->buttons_input = buttons;
+				first_button = false;
+			}
+
+			input_set_capability(buttons, EV_KEY, keycode);
+		} else if (!strcmp(sensor->name, "slider") ||
+			   !strcmp(sensor->name, "wheel")) {
+			u32 scroller_id;
+			struct input_dev *scroller;
+
+			if (of_property_read_u32(sensor, "reg", &scroller_id)) {
+				dev_err(ptc->dev, "reg is missing (%s)\n",
+					sensor->full_name);
+				return -EINVAL;
+			}
+
+			if (scroller_id > ATMEL_PTC_MAX_SCROLLERS - 1) {
+				dev_err(ptc->dev, "wrong scroller id (%s)\n",
+					sensor->full_name);
+				return -EINVAL;
+			}
+
+			scroller = devm_input_allocate_device(ptc->dev);
+			if (!scroller)
+				return -ENOMEM;
+
+			scroller->dev.parent = ptc->dev;
+			ptc->scroller_input[scroller_id] = scroller;
+
+			if (!strcmp(sensor->name, "slider")) {
+				scroller->name = "atmel_ptc_slider";
+				input_set_capability(scroller, EV_ABS, ABS_X);
+				input_set_capability(scroller, EV_KEY, BTN_TOUCH);
+			} else {
+				scroller->name = "atmel_ptc_wheel";
+				input_set_capability(scroller, EV_ABS, ABS_WHEEL);
+				input_set_capability(scroller, EV_KEY, BTN_TOUCH);
+			}
+		} else {
+			dev_err(ptc->dev, "%s is not supported\n", sensor->name);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void atmel_qtm_conf_callback(const struct firmware *conf, void *context)
+{
+	struct atmel_ptc *ptc = context;
+	struct atmel_qtm_cmd qtm_cmd;
+	char *dst;
+	struct atmel_qtm_node_group_config node_group_config;
+
+	if (!conf) {
+		dev_err(ptc->dev, "cannot load QTM configuration, it has to be set manually.\n");
+		return;
+	}
+
+	atmel_ppp_irq_enable(ptc, ATMEL_PPP_IRQ1);
+	atmel_ppp_irq_disable(ptc, ATMEL_PPP_IRQ2 | ATMEL_PPP_IRQ3);
+
+	qtm_cmd.id = ATMEL_QTM_CMD_STOP;
+	atmel_qtm_cmd_send(ptc, &qtm_cmd);
+
+	/* Load QTM configuration. */
+	dst = (char *)ptc->qtm_mb + ATMEL_QTM_MB_NODE_GROUP_CONFIG_OFFSET;
+	/* memcpy doesn't work for an unknown reason. */
+	_memcpy_toio(dst, conf->data, conf->size);
+	release_firmware(conf);
+
+	if (atmel_ptc_of_parse(ptc))
+		dev_err(ptc->dev, "ptc_of_parse failed\n");
+
+	memcpy(&node_group_config,
+	       ptc->qtm_mb + ATMEL_QTM_MB_NODE_GROUP_CONFIG_OFFSET,
+	       sizeof(node_group_config));
+
+	/* Start QTM. */
+	qtm_cmd.id = ATMEL_QTM_CMD_INIT;
+	qtm_cmd.data = node_group_config.count;
+	atmel_qtm_cmd_send(ptc, &qtm_cmd);
+	qtm_cmd.id = ATMEL_QTM_CMD_SET_ACQ_MODE_TIMER;
+	qtm_cmd.data = 20;
+	atmel_qtm_cmd_send(ptc, &qtm_cmd);
+	qtm_cmd.id = ATMEL_QTM_CMD_RUN;
+	qtm_cmd.data = node_group_config.count;
+	atmel_qtm_cmd_send(ptc, &qtm_cmd);
+}
+
+static void atmel_ppp_fw_callback(const struct firmware *fw, void *context)
+{
+	struct atmel_ptc *ptc = context;
+	int ret;
+	struct atmel_qtm_cmd cmd;
+
+	if (!fw || !fw->size) {
+		dev_err(ptc->dev, "cannot load firmware.\n");
+		release_firmware(fw);
+		device_release_driver(ptc->dev);
+		return;
+	}
+
+	/* Command sequence to start from a clean state. */
+	atmel_ppp_cmd_send(ptc, ATMEL_PPP_CMD_ABORT);
+	atmel_ppp_irq_pending_clr(ptc, ATMEL_PPP_IRQ_MASK);
+	atmel_ppp_cmd_send(ptc, ATMEL_PPP_CMD_RESET);
+
+	memcpy(ptc->firmware, fw->data, fw->size);
+	release_firmware(fw);
+
+	atmel_ppp_cmd_send(ptc, ATMEL_PPP_CMD_RUN);
+
+	cmd.id = ATMEL_QTM_CMD_FIRM_VERSION;
+	atmel_qtm_cmd_send(ptc, &cmd);
+	dev_info(ptc->dev, "firmware version: %u\n", cmd.data);
+
+	/* PPP is running, it's time to load the QTM configuration. */
+	ret = request_firmware_nowait(THIS_MODULE, 1, ATMEL_QTM_CONF_NAME, ptc->dev,
+				      GFP_KERNEL, ptc, atmel_qtm_conf_callback);
+	if (ret)
+		dev_err(ptc->dev, "QTM configuration loading failed.\n");
+}
+
+static int atmel_ptc_probe(struct platform_device *pdev)
+{
+	struct atmel_ptc *ptc;
+	struct resource	*res;
+	void *shared_memory;
+	int ret;
+
+	ptc = devm_kzalloc(&pdev->dev, sizeof(*ptc), GFP_KERNEL);
+	if (!ptc)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, ptc);
+	ptc->dev = &pdev->dev;
+	ptc->dev->driver_data = ptc;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	shared_memory = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(shared_memory))
+		return PTR_ERR(shared_memory);
+
+	ptc->firmware = shared_memory;
+	ptc->qtm_mb = shared_memory + ATMEL_QTM_MB_OFFSET;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res)
+		return -EINVAL;
+
+	ptc->ppp_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ptc->ppp_regs))
+		return PTR_ERR(ptc->ppp_regs);
+
+	ptc->irq = platform_get_irq(pdev, 0);
+	if (ptc->irq <= 0) {
+		if (!ptc->irq)
+			ptc->irq = -ENXIO;
+
+		return ptc->irq;
+	}
+
+	ptc->clk_per = devm_clk_get(&pdev->dev, "ptc_clk");
+	if (IS_ERR(ptc->clk_per))
+		return PTR_ERR(ptc->clk_per);
+
+	ptc->clk_int_osc = devm_clk_get(&pdev->dev, "ptc_int_osc");
+	if (IS_ERR(ptc->clk_int_osc))
+		return PTR_ERR(ptc->clk_int_osc);
+
+	ptc->clk_slow = devm_clk_get(&pdev->dev, "slow_clk");
+	if (IS_ERR(ptc->clk_slow))
+		return PTR_ERR(ptc->clk_slow);
+
+	ret = devm_request_irq(&pdev->dev, ptc->irq, atmel_ppp_irq_handler, 0,
+			       pdev->dev.driver->name, ptc);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(ptc->clk_int_osc);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(ptc->clk_per);
+	if (ret)
+		goto disable_clk_int_osc;
+
+	ret = clk_prepare_enable(ptc->clk_slow);
+	if (ret)
+		goto disable_clk_per;
+
+	/* Needed to avoid unexpected behaviors. */
+	memset(ptc->firmware, 0, ATMEL_QTM_MB_OFFSET + sizeof(*ptc->qtm_mb));
+	ptc->imr = 0;
+	init_completion(&ptc->ppp_ack);
+
+	/*
+	 * Expose a file to give an access to the QTM mailbox to a user space
+	 * application in order to configure it or to send commands.
+	 */
+	ret = sysfs_create_bin_file(&pdev->dev.kobj, &atmel_ptc_qtm_mb_attr);
+	if (ret)
+		goto disable_clk_slow;
+
+	ret = request_firmware_nowait(THIS_MODULE, 1, ATMEL_PPP_FIRMWARE_NAME,
+				      ptc->dev, GFP_KERNEL, ptc,
+				      atmel_ppp_fw_callback);
+	if (ret) {
+		dev_err(&pdev->dev, "firmware loading failed\n");
+		ret = -EPROBE_DEFER;
+		goto remove_qtm_mb;
+	}
+
+	return 0;
+
+remove_qtm_mb:
+	sysfs_remove_bin_file(&pdev->dev.kobj, &atmel_ptc_qtm_mb_attr);
+disable_clk_slow:
+	clk_disable_unprepare(ptc->clk_slow);
+disable_clk_per:
+	clk_disable_unprepare(ptc->clk_per);
+disable_clk_int_osc:
+	clk_disable_unprepare(ptc->clk_int_osc);
+
+	return ret;
+}
+
+static int atmel_ptc_remove(struct platform_device *pdev)
+{
+	struct atmel_ptc *ptc = platform_get_drvdata(pdev);
+	int i;
+
+	if (ptc->buttons_registered)
+		input_unregister_device(ptc->buttons_input);
+
+	for (i = 0; i < ATMEL_PTC_MAX_SCROLLERS; i++) {
+		struct input_dev *scroller = ptc->scroller_input[i];
+
+		if (!scroller || !ptc->scroller_registered[i])
+			continue;
+		input_unregister_device(scroller);
+	}
+
+	sysfs_remove_bin_file(&pdev->dev.kobj, &atmel_ptc_qtm_mb_attr);
+	clk_disable_unprepare(ptc->clk_slow);
+	clk_disable_unprepare(ptc->clk_per);
+	clk_disable_unprepare(ptc->clk_int_osc);
+
+	return 0;
+}
+
+static const struct of_device_id atmel_ptc_dt_match[] = {
+	{
+		.compatible = "atmel,sama5d2-ptc",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, atmel_ptc_dt_match);
+
+static struct platform_driver atmel_ptc_driver = {
+	.probe = atmel_ptc_probe,
+	.remove = atmel_ptc_remove,
+	.driver = {
+		.name = "atmel_ptc",
+		.of_match_table = atmel_ptc_dt_match,
+	},
+};
+module_platform_driver(atmel_ptc_driver)
+
+MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@microchip.com>");
+MODULE_DESCRIPTION("Atmel PTC subsystem");
+MODULE_LICENSE("GPL v2");
+MODULE_FIRMWARE(ATMEL_PPP_FIRMWARE_NAME);
+MODULE_FIRMWARE(ATMEL_QTM_CONF_NAME);
-- 
2.9.0

^ permalink raw reply related

* [PATCH v2 1/5] dt-bindings: input: Add Atmel PTC subsystem bindings
From: Ludovic Desroches @ 2017-04-21  8:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170421083924.15096-1-ludovic.desroches@microchip.com>

Add description of the Atmel PTC subsystem bindings.

Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com>
Acked-by: Rob Herring <robh@kernel.org>
---
 .../devicetree/bindings/input/atmel,ptc.txt        | 67 ++++++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/atmel,ptc.txt

diff --git a/Documentation/devicetree/bindings/input/atmel,ptc.txt b/Documentation/devicetree/bindings/input/atmel,ptc.txt
new file mode 100644
index 0000000..a183fd5
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/atmel,ptc.txt
@@ -0,0 +1,67 @@
+Atmel PTC Subsystem
+
+The Atmel Peripheral Touch Controller subsystem offers built-in hardware
+for capacitive touch measurement on sensors that function as buttons, sliders
+and wheels.
+
+1) PTC Subsystem node
+
+Required properties:
+- compatible: 		Must be "atmel,sama5d2-ptc"
+- reg: 			Address, length of the shared memory and ppp registers location
+			and length.
+- clocks: 		Phandlers to the clocks.
+- clock-names: 		Must be "ptc_clk", "ptc_int_osc", "slow_clk".
+- #address-cells:	Must be one. The cell is the button or scroller id.
+- #size-cells: 		Must be zero.
+
+Example:
+	ptc at fc060000 {
+		compatible = "atmel,sama5d2-ptc";
+		reg = <0x00800000 0x10000
+		       0xfc060000 0xcf>;
+		interrupts = <58 IRQ_TYPE_LEVEL_HIGH 7>;
+		clocks = <&ptc_clk>, <&main>, <&clk32k>;
+		clock-names = "ptc_clk", "ptc_int_osc", "slow_clk";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		[ child node definitions... ]
+	};
+
+2) Scroller / buttons subnodes
+
+Subnodes describe the kind of sensors the customer want to use. They have to be
+named according to their function: button, slider or wheel.
+
+2.1) Scroller subnodes
+
+Required properties:
+- reg:	Id of the scroller, each id must be different.
+
+Example:
+	slider at 0 {
+		reg = <0>;
+	};
+
+	wheel at 1 {
+		reg = <1>;
+	};
+
+2.2) Button subnodes
+
+Required properties:
+- reg:			Id of node used for the button, each id must be
+			different.
+- linux,keycode: 	Key code of the button.
+
+Example:
+		button at 8 {
+			reg = <8>;
+			linux,keycode = <2>;
+		};
+
+		button at 9 {
+			reg = <9>;
+			linux,keycode = <3>;
+		};
-- 
2.9.0

^ permalink raw reply related

* [PATCH v2 0/5] Introduce the Atmel PTC subsystem
From: Ludovic Desroches @ 2017-04-21  8:39 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

The Atmel Peripheral touch controller subsystem offers built-in hardware for
capacitive touch measurement on sensors that function as buttons, sliders and
wheels. It is available on SAMA5D2.

A firmware and a configuration file describing the topology and the parameters
of the sensor are loaded when probing the driver.

Changes:
- v2:
  - reorder patches to get the bindings documentation in first
  - remove the header from the uapi since it may change in the future. Declare
  only the few structures needed in the driver.
  - add this driver to the sama5_defconfig

Ludovic Desroches (5):
  dt-bindings: input: Add Atmel PTC subsystem bindings
  input: misc: introduce Atmel PTC driver
  MAINTAINERS: add Atmel PTC entries
  ARM: dts: at91: sama5d2: add PTC subsystem device
  ARM: at91/defconfig: add PTC driver to sama5_defconfig

 .../devicetree/bindings/input/atmel,ptc.txt        |  67 ++
 MAINTAINERS                                        |   7 +
 arch/arm/boot/dts/sama5d2.dtsi                     |  16 +
 arch/arm/configs/sama5_defconfig                   |   2 +
 drivers/input/misc/Kconfig                         |  12 +
 drivers/input/misc/Makefile                        |   1 +
 drivers/input/misc/atmel_ptc.c                     | 723 +++++++++++++++++++++
 7 files changed, 828 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/atmel,ptc.txt
 create mode 100644 drivers/input/misc/atmel_ptc.c

-- 
2.9.0

^ permalink raw reply

* [PATCH v2 9/9] ARM: dts: sun6i: Enable tcon0 by default
From: Chen-Yu Tsai @ 2017-04-21  8:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170421083857.29636-1-wens@csie.org>

tcon0 contains a muxing register used to mux tcon output to downstream
hdmi or mipi dsi encoders. tcon0 must be available for the mux to be
configured.

Whether the display subsystem is enabled or not is now solely controlled
by the display-engine node.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun6i-a31-hummingbird.dts | 1 -
 arch/arm/boot/dts/sun6i-a31.dtsi            | 1 -
 2 files changed, 2 deletions(-)

diff --git a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
index b4c87a23e3f8..55607208468b 100644
--- a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
+++ b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
@@ -319,7 +319,6 @@
 &tcon0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&lcd0_rgb888_pins>;
-	status = "okay";
 };
 
 &tcon0_out {
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index dfcf64515d7f..d0cede5aaeb5 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -264,7 +264,6 @@
 				      "tcon-ch0",
 				      "tcon-ch1";
 			clock-output-names = "tcon0-pixel-clock";
-			status = "disabled";
 
 			ports {
 				#address-cells = <1>;
-- 
2.11.0

^ permalink raw reply related

* [PATCH v2 8/9] ARM: dts: sun6i: Add second display pipeline device nodes
From: Chen-Yu Tsai @ 2017-04-21  8:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170421083857.29636-1-wens@csie.org>

The Allwinner A31/A31s SoCs have 2 display pipelines, as in 2 display
frontends, backends, and tcons each. The relationship between the
backends and tcons are 1:1, but the frontends can feed either backend.

Add device nodes and of graph nodes describing this relationship.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun6i-a31.dtsi | 168 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 167 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index 9c999d3788f6..dfcf64515d7f 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -232,7 +232,7 @@
 
 	de: display-engine {
 		compatible = "allwinner,sun6i-a31-display-engine";
-		allwinner,pipelines = <&fe0>;
+		allwinner,pipelines = <&fe0>, <&fe1>;
 		status = "disabled";
 	};
 
@@ -289,6 +289,43 @@
 			};
 		};
 
+		tcon1: lcd-controller at 01c0d000 {
+			compatible = "allwinner,sun6i-a31-tcon";
+			reg = <0x01c0d000 0x1000>;
+			interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+			resets = <&ccu RST_AHB1_LCD1>;
+			reset-names = "lcd";
+			clocks = <&ccu CLK_AHB1_LCD1>,
+				 <&ccu CLK_LCD1_CH0>,
+				 <&ccu CLK_LCD1_CH1>;
+			clock-names = "ahb",
+				      "tcon-ch0",
+				      "tcon-ch1";
+			clock-output-names = "tcon1-pixel-clock";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				tcon1_in: port at 0 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <0>;
+
+					tcon1_in_drc1: endpoint at 0 {
+						reg = <0>;
+						remote-endpoint = <&drc1_out_tcon1>;
+					};
+				};
+
+				tcon1_out: port at 1 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <1>;
+				};
+			};
+		};
+
 		mmc0: mmc at 01c0f000 {
 			compatible = "allwinner,sun7i-a20-mmc";
 			reg = <0x01c0f000 0x1000>;
@@ -896,6 +933,130 @@
 						reg = <0>;
 						remote-endpoint = <&be0_in_fe0>;
 					};
+
+					fe0_out_be1: endpoint at 1 {
+						reg = <1>;
+						remote-endpoint = <&be1_in_fe0>;
+					};
+				};
+			};
+		};
+
+		fe1: display-frontend at 01e20000 {
+			compatible = "allwinner,sun6i-a31-display-frontend";
+			reg = <0x01e20000 0x20000>;
+			interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_AHB1_FE1>, <&ccu CLK_FE1>,
+				 <&ccu CLK_DRAM_FE1>;
+			clock-names = "ahb", "mod",
+				      "ram";
+			resets = <&ccu RST_AHB1_FE1>;
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				fe1_out: port at 1 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <1>;
+
+					fe1_out_be0: endpoint at 0 {
+						reg = <0>;
+						remote-endpoint = <&be0_in_fe1>;
+					};
+
+					fe1_out_be1: endpoint at 1 {
+						reg = <1>;
+						remote-endpoint = <&be1_in_fe1>;
+					};
+				};
+			};
+		};
+
+		be1: display-backend at 01e40000 {
+			compatible = "allwinner,sun6i-a31-display-backend";
+			reg = <0x01e40000 0x10000>;
+			interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_AHB1_BE1>, <&ccu CLK_BE1>,
+				 <&ccu CLK_DRAM_BE1>;
+			clock-names = "ahb", "mod",
+				      "ram";
+			resets = <&ccu RST_AHB1_BE1>;
+
+			assigned-clocks = <&ccu CLK_BE1>;
+			assigned-clock-rates = <300000000>;
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				be1_in: port at 0 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <0>;
+
+					be1_in_fe0: endpoint at 0 {
+						reg = <0>;
+						remote-endpoint = <&fe0_out_be1>;
+					};
+
+					be1_in_fe1: endpoint at 1 {
+						reg = <1>;
+						remote-endpoint = <&fe1_out_be1>;
+					};
+				};
+
+				be1_out: port at 1 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <1>;
+
+					be1_out_drc1: endpoint at 0 {
+						reg = <0>;
+						remote-endpoint = <&drc1_in_be1>;
+					};
+				};
+			};
+		};
+
+		drc1: drc at 01e50000 {
+			compatible = "allwinner,sun6i-a31-drc";
+			reg = <0x01e50000 0x10000>;
+			interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_AHB1_DRC1>, <&ccu CLK_IEP_DRC1>,
+				 <&ccu CLK_DRAM_DRC1>;
+			clock-names = "ahb", "mod",
+				      "ram";
+			resets = <&ccu RST_AHB1_DRC1>;
+
+			assigned-clocks = <&ccu CLK_IEP_DRC1>;
+			assigned-clock-rates = <300000000>;
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				drc1_in: port at 0 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <0>;
+
+					drc1_in_be1: endpoint at 0 {
+						reg = <0>;
+						remote-endpoint = <&be1_out_drc1>;
+					};
+				};
+
+				drc1_out: port at 1 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <1>;
+
+					drc1_out_tcon1: endpoint at 0 {
+						reg = <0>;
+						remote-endpoint = <&tcon1_in_drc1>;
+					};
 				};
 			};
 		};
@@ -926,6 +1087,11 @@
 						reg = <0>;
 						remote-endpoint = <&fe0_out_be0>;
 					};
+
+					be0_in_fe1: endpoint at 1 {
+						reg = <1>;
+						remote-endpoint = <&fe1_out_be0>;
+					};
 				};
 
 				be0_out: port at 1 {
-- 
2.11.0

^ permalink raw reply related

* [PATCH v2 7/9] drm/sun4i: tcon: Copy ID from associated backend
From: Chen-Yu Tsai @ 2017-04-21  8:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170421083857.29636-1-wens@csie.org>

The tcons and backends have a one-to-one relationship. Their IDs,
or indexes in the documentation, are also the same.

Copy the ID from the associated backend and save it in the tcon
structure. This will later be used when we add support for the
output data path muxes.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 drivers/gpu/drm/sun4i/sun4i_tcon.c | 1 +
 drivers/gpu/drm/sun4i/sun4i_tcon.h | 2 ++
 2 files changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 4409e7b6c74d..8301389c411d 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -540,6 +540,7 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
 	dev_set_drvdata(dev, tcon);
 	tcon->drm = drm;
 	tcon->dev = dev;
+	tcon->id = backend->id;
 	tcon->quirks = of_device_get_match_data(dev);
 
 	tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h
index 1bda4d183eec..d37e1e2ed60e 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h
@@ -174,6 +174,8 @@ struct sun4i_tcon {
 	/* Associated crtc */
 	struct sun4i_crtc		*crtc;
 
+	int				id;
+
 	/* TCON list management */
 	struct list_head		list;
 };
-- 
2.11.0

^ permalink raw reply related

* [PATCH v2 6/9] drm/sun4i: tcon: Find matching display backend by device node matching
From: Chen-Yu Tsai @ 2017-04-21  8:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170421083857.29636-1-wens@csie.org>

With Allwinner's Display Engine 1.0, each TCON's input is tied to a
specific display backend, and the 2 comprise what is known as a crtc
in DRM KMS land: The layer, framebuffer, and compositing functions are
provided by the backend, while the TCON provides the display timing
signals and vblank interrupts. This 1 to 1 relationship is represented
in the device tree. On some systems there is an intermediate DRC
component.

Pointers to both matching components must be provided when initializing
the crtc. As the backend is always registered before the associated
tcon, we can recursively search upwards through the of_graph to find
the matching backend.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 drivers/gpu/drm/sun4i/sun4i_tcon.c | 55 +++++++++++++++++++++++++++++++++++---
 1 file changed, 51 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 52f37ef9a050..4409e7b6c74d 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -472,6 +472,53 @@ struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node)
 	return of_drm_find_bridge(remote) ?: ERR_PTR(-EPROBE_DEFER);
 }
 
+/*
+ * On SoCs with the old display pipeline design (Display Engine 1.0),
+ * the TCON is always tied to just one backend. Hence we can traverse
+ * the of_graph upwards to find the backend our tcon is connected to,
+ * and take its ID as our own.
+ *
+ * We can either identify backends from their compatible strings, which
+ * means maintaining a large list of them. Or, since the backend is
+ * registered and binded before the TCON, we can just go through the
+ * list of registered backends and compare the device node.
+ */
+static struct sun4i_backend *sun4i_tcon_find_backend(struct sun4i_drv *drv,
+						     struct device_node *node)
+{
+	struct device_node *port, *ep, *remote;
+	struct sun4i_backend *backend;
+
+	port = of_graph_get_port_by_id(node, 0);
+	if (!port)
+		return ERR_PTR(-EINVAL);
+
+	for_each_available_child_of_node(port, ep) {
+		remote = of_graph_get_remote_port_parent(ep);
+		if (!remote)
+			continue;
+
+		/* does this node match any registered backends? */
+		list_for_each_entry(backend, &drv->backend_list, list) {
+			if (remote == backend->node) {
+				of_node_put(remote);
+				of_node_put(port);
+				return backend;
+			}
+		}
+
+		/* keep looking through upstream ports */
+		backend = sun4i_tcon_find_backend(drv, remote);
+		if (!IS_ERR(backend)) {
+			of_node_put(remote);
+			of_node_put(port);
+			return backend;
+		}
+	}
+
+	return ERR_PTR(-EINVAL);
+}
+
 static int sun4i_tcon_bind(struct device *dev, struct device *master,
 			   void *data)
 {
@@ -481,9 +528,11 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
 	struct sun4i_tcon *tcon;
 	int ret;
 
-	/* Wait for a backend to be registered */
-	if (list_empty(&drv->backend_list))
+	backend = sun4i_tcon_find_backend(drv, dev->of_node);
+	if (IS_ERR(backend)) {
+		dev_err(dev, "Couldn't find matching backend\n");
 		return -EPROBE_DEFER;
+	}
 
 	tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
 	if (!tcon)
@@ -533,8 +582,6 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
 		goto err_free_dotclock;
 	}
 
-	backend = list_first_entry(&drv->backend_list,
-				   struct sun4i_backend, list);
 	tcon->crtc = sun4i_crtc_init(drm, backend, tcon);
 	if (IS_ERR(tcon->crtc)) {
 		dev_err(dev, "Couldn't create our CRTC\n");
-- 
2.11.0

^ permalink raw reply related


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