LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] powerpc/kprobes: Fix trap address when trap happened in real mode
From: Masami Hiramatsu @ 2020-02-18  0:44 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: linux-kernel, Anil S Keshavamurthy, Paul Mackerras, stable,
	Naveen N. Rao, linuxppc-dev, David S. Miller, Larry Finger
In-Reply-To: <c6257b49-bf02-d30a-1e2e-99abba5955e6@c-s.fr>

On Mon, 17 Feb 2020 16:38:50 +0100
Christophe Leroy <christophe.leroy@c-s.fr> wrote:

> 
> 
> Le 17/02/2020 à 11:27, Masami Hiramatsu a écrit :
> > On Mon, 17 Feb 2020 10:03:22 +0100
> > Christophe Leroy <christophe.leroy@c-s.fr> wrote:
> > 
> >>
> >>
> >> Le 16/02/2020 à 13:34, Masami Hiramatsu a écrit :
> >>> On Sat, 15 Feb 2020 11:28:49 +0100
> >>> Christophe Leroy <christophe.leroy@c-s.fr> wrote:
> >>>
> >>>> Hi,
> >>>>
> >>>> Le 14/02/2020 à 14:54, Masami Hiramatsu a écrit :
> >>>>> Hi,
> >>>>>
> >>>>> On Fri, 14 Feb 2020 12:47:49 +0000 (UTC)
> >>>>> Christophe Leroy <christophe.leroy@c-s.fr> wrote:
> >>>>>
> >>>>>> When a program check exception happens while MMU translation is
> >>>>>> disabled, following Oops happens in kprobe_handler() in the following
> >>>>>> test:
> >>>>>>
> >>>>>> 		} else if (*addr != BREAKPOINT_INSTRUCTION) {
> >>>>>
> >>>>> Thanks for the report and patch. I'm not so sure about powerpc implementation
> >>>>> but at where the MMU translation is disabled, can the handler work correctly?
> >>>>> (And where did you put the probe on?)
> >>>>>
> >>>>> Your fix may fix this Oops, but if the handler needs special care, it is an
> >>>>> option to blacklist such place (if possible).
> >>>>
> >>>> I guess that's another story. Here we are not talking about a place
> >>>> where kprobe has been illegitimately activated, but a place where there
> >>>> is a valid trap, which generated a valid 'program check exception'. And
> >>>> kprobe was off at that time.
> >>>
> >>> Ah, I got it. It is not a kprobe breakpoint, but to check that correctly,
> >>> it has to know the address where the breakpoint happens. OK.
> >>>
> >>>>
> >>>> As any 'program check exception' due to a trap (ie a BUG_ON, a WARN_ON,
> >>>> a debugger breakpoint, a perf breakpoint, etc...) calls
> >>>> kprobe_handler(), kprobe_handler() must be prepared to handle the case
> >>>> where the MMU translation is disabled, even if probes are not supposed
> >>>> to be set for functions running with MMU translation disabled.
> >>>
> >>> Can't we check the MMU is disabled there (as same as checking the exception
> >>> happened in user space or not)?
> >>>
> >>
> >> What do you mean by 'there' ? At the entry of kprobe_handler() ?
> >>
> >> That's what my patch does, it checks whether MMU is disabled or not. If
> >> it is, it converts the address to a virtual address.
> >>
> >> Do you mean kprobe_handler() should bail out early as it does when the
> >> trap happens in user mode ?
> > 
> > Yes, that is what I meant.
> > 
> >> Of course we can do that, I don't know
> >> enough about kprobe to know if kprobe_handler() should manage events
> >> that happened in real-mode or just ignore them. But I tested adding an
> >> event on a function that runs in real-mode, and it (now) works.
> >>
> >> So, what should we do really ?
> > 
> > I'm not sure how the powerpc kernel runs in real mode.
> > But clearly, at least kprobe event can not handle that case because
> > it tries to access memory by probe_kernel_read(). Unless that function
> > correctly handles the address translation, I want to prohibit kprobes
> > on such address.
> > 
> > So what I would like to see is, something like below.
> > 
> > diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c
> > index 2d27ec4feee4..4771be152416 100644
> > --- a/arch/powerpc/kernel/kprobes.c
> > +++ b/arch/powerpc/kernel/kprobes.c
> > @@ -261,7 +261,7 @@ int kprobe_handler(struct pt_regs *regs)
> >          unsigned int *addr = (unsigned int *)regs->nip;
> >          struct kprobe_ctlblk *kcb;
> >   
> > -       if (user_mode(regs))
> > +       if (user_mode(regs) || !(regs->msr & MSR_IR))
> >                  return 0;
> >   
> >          /*
> > 
> > 
> 
> With this instead change of my patch, I get an Oops everytime a kprobe 
> event occurs in real-mode.
> 
> This is because kprobe_handler() is now saying 'this trap doesn't belong 
> to me' for a trap that has been installed by it.

Hmm, on powerpc, kprobes is allowed to probe on the code which runs
in the real mode? I think we should also prohibit it by blacklisting.
(It is easy to add blacklist by NOKPROBE_SYMBOL(func))
Or, some parts are possble to run under both real mode and kernel mode?

> 
> So the 'program check' exception handler doesn't find the owner of the 
> trap hence generate an Oops.
> 
> Even if we don't want kprobe() to proceed with the event entirely 
> (allthough it works at least for simple events), I'd expect it to fail 
> gracefully.

Agreed. I thought it was easy to identify real mode code. But if it is
hard, we should apply your first patch and also skip user handlers
if we are in the real mode (and increment missed count).

BTW, can the emulater handle the real mode code correctly?

Thank you,

-- 
Masami Hiramatsu <mhiramat@kernel.org>

^ permalink raw reply

* Re: [PATCH 2/5] mm/vma: Make vma_is_accessible() available for general use
From: Guo Ren @ 2020-02-18  2:21 UTC (permalink / raw)
  To: Anshuman Khandual
  Cc: Rich Felker, linux-sh, Peter Zijlstra, Dave Hansen, linux-mips,
	linux-mm, Paul Mackerras, Yoshinori Sato, Ingo Molnar,
	Geert Uytterhoeven, Mel Gorman, Steven Rostedt, linux-m68k,
	Andy Lutomirski, Thomas Gleixner, Paul Burton,
	Linux Kernel Mailing List, Ralf Baechle, Andrew Morton,
	linuxppc-dev
In-Reply-To: <1581915833-21984-3-git-send-email-anshuman.khandual@arm.com>

csky:

Acked-by: Guo Ren <guoren@kernel.org>


On Mon, Feb 17, 2020 at 1:04 PM Anshuman Khandual
<anshuman.khandual@arm.com> wrote:
>
> Lets move vma_is_accessible() helper to include/linux/mm.h which makes it
> available for general use. While here, this replaces all remaining open
> encodings for VMA access check with vma_is_accessible().
>
> Cc: Guo Ren <guoren@kernel.org>
> Cc: Geert Uytterhoeven <geert@linux-m68k.org
> Cc: Ralf Baechle <ralf@linux-mips.org>
> Cc: Paul Burton <paulburton@kernel.org>
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Cc: Paul Mackerras <paulus@samba.org>
> Cc: Michael Ellerman <mpe@ellerman.id.au>
> Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
> Cc: Rich Felker <dalias@libc.org>
> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: Andy Lutomirski <luto@kernel.org>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Mel Gorman <mgorman@suse.de>
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-m68k@lists.linux-m68k.org
> Cc: linux-mips@vger.kernel.org
> Cc: linuxppc-dev@lists.ozlabs.org
> Cc: linux-sh@vger.kernel.org
> Cc: linux-mm@kvack.org
> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
> ---
>  arch/csky/mm/fault.c    | 2 +-
>  arch/m68k/mm/fault.c    | 2 +-
>  arch/mips/mm/fault.c    | 2 +-
>  arch/powerpc/mm/fault.c | 2 +-
>  arch/sh/mm/fault.c      | 2 +-
>  arch/x86/mm/fault.c     | 2 +-
>  include/linux/mm.h      | 5 +++++
>  kernel/sched/fair.c     | 2 +-
>  mm/gup.c                | 2 +-
>  mm/memory.c             | 5 -----
>  mm/mempolicy.c          | 3 +--
>  11 files changed, 14 insertions(+), 15 deletions(-)
>
> diff --git a/arch/csky/mm/fault.c b/arch/csky/mm/fault.c
> index f76618b630f9..4b3511b8298d 100644
> --- a/arch/csky/mm/fault.c
> +++ b/arch/csky/mm/fault.c
> @@ -137,7 +137,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
>                 if (!(vma->vm_flags & VM_WRITE))
>                         goto bad_area;
>         } else {
> -               if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
> +               if (!vma_is_accessible(vma))
>                         goto bad_area;
>         }
>
> diff --git a/arch/m68k/mm/fault.c b/arch/m68k/mm/fault.c
> index e9b1d7585b43..d5131ec5d923 100644
> --- a/arch/m68k/mm/fault.c
> +++ b/arch/m68k/mm/fault.c
> @@ -125,7 +125,7 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,
>                 case 1:         /* read, present */
>                         goto acc_err;
>                 case 0:         /* read, not present */
> -                       if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
> +                       if (!vma_is_accessible(vma))
>                                 goto acc_err;
>         }
>
> diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c
> index 1e8d00793784..5b9f947bfa32 100644
> --- a/arch/mips/mm/fault.c
> +++ b/arch/mips/mm/fault.c
> @@ -142,7 +142,7 @@ static void __kprobes __do_page_fault(struct pt_regs *regs, unsigned long write,
>                                 goto bad_area;
>                         }
>                 } else {
> -                       if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
> +                       if (!vma_is_accessible(vma))
>                                 goto bad_area;
>                 }
>         }
> diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
> index 8db0507619e2..71a3658c516b 100644
> --- a/arch/powerpc/mm/fault.c
> +++ b/arch/powerpc/mm/fault.c
> @@ -314,7 +314,7 @@ static bool access_error(bool is_write, bool is_exec,
>                 return false;
>         }
>
> -       if (unlikely(!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))))
> +       if (unlikely(!vma_is_accessible(vma)))
>                 return true;
>         /*
>          * We should ideally do the vma pkey access check here. But in the
> diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c
> index 5f51456f4fc7..a8c4253f37d7 100644
> --- a/arch/sh/mm/fault.c
> +++ b/arch/sh/mm/fault.c
> @@ -355,7 +355,7 @@ static inline int access_error(int error_code, struct vm_area_struct *vma)
>                 return 1;
>
>         /* read, not present: */
> -       if (unlikely(!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))))
> +       if (unlikely(!vma_is_accessible(vma)))
>                 return 1;
>
>         return 0;
> diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
> index fa4ea09593ab..c461eaab0306 100644
> --- a/arch/x86/mm/fault.c
> +++ b/arch/x86/mm/fault.c
> @@ -1200,7 +1200,7 @@ access_error(unsigned long error_code, struct vm_area_struct *vma)
>                 return 1;
>
>         /* read, not present: */
> -       if (unlikely(!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))))
> +       if (unlikely(!vma_is_accessible(vma)))
>                 return 1;
>
>         return 0;
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 52269e56c514..b0e53ef13ff1 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -541,6 +541,11 @@ static inline bool vma_is_anonymous(struct vm_area_struct *vma)
>         return !vma->vm_ops;
>  }
>
> +static inline bool vma_is_accessible(struct vm_area_struct *vma)
> +{
> +       return vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC);
> +}
> +
>  #ifdef CONFIG_SHMEM
>  /*
>   * The vma_is_shmem is not inline because it is used only by slow
> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index fe4e0d775375..6ce54d57dd09 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -2573,7 +2573,7 @@ static void task_numa_work(struct callback_head *work)
>                  * Skip inaccessible VMAs to avoid any confusion between
>                  * PROT_NONE and NUMA hinting ptes
>                  */
> -               if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
> +               if (!vma_is_accessible(vma))
>                         continue;
>
>                 do {
> diff --git a/mm/gup.c b/mm/gup.c
> index 1b521e0ac1de..c8ffe2e61f03 100644
> --- a/mm/gup.c
> +++ b/mm/gup.c
> @@ -1171,7 +1171,7 @@ long populate_vma_page_range(struct vm_area_struct *vma,
>          * We want mlock to succeed for regions that have any permissions
>          * other than PROT_NONE.
>          */
> -       if (vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC))
> +       if (vma_is_accessible(vma))
>                 gup_flags |= FOLL_FORCE;
>
>         /*
> diff --git a/mm/memory.c b/mm/memory.c
> index 0bccc622e482..2f07747612b7 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -3942,11 +3942,6 @@ static inline vm_fault_t wp_huge_pmd(struct vm_fault *vmf, pmd_t orig_pmd)
>         return VM_FAULT_FALLBACK;
>  }
>
> -static inline bool vma_is_accessible(struct vm_area_struct *vma)
> -{
> -       return vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE);
> -}
> -
>  static vm_fault_t create_huge_pud(struct vm_fault *vmf)
>  {
>  #ifdef CONFIG_TRANSPARENT_HUGEPAGE
> diff --git a/mm/mempolicy.c b/mm/mempolicy.c
> index 977c641f78cf..91c1ad6ab8ea 100644
> --- a/mm/mempolicy.c
> +++ b/mm/mempolicy.c
> @@ -649,8 +649,7 @@ static int queue_pages_test_walk(unsigned long start, unsigned long end,
>
>         if (flags & MPOL_MF_LAZY) {
>                 /* Similar to task_numa_work, skip inaccessible VMAs */
> -               if (!is_vm_hugetlb_page(vma) &&
> -                       (vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)) &&
> +               if (!is_vm_hugetlb_page(vma) && vma_is_accessible(vma) &&
>                         !(vma->vm_flags & VM_MIXEDMAP))
>                         change_prot_numa(vma, start, endvma);
>                 return 1;
> --
> 2.20.1
>


-- 
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/

^ permalink raw reply

* [PATCH] KVM: PPC: Book3S HV: Use RADIX_PTE_INDEX_SIZE in Radix MMU code
From: Michael Ellerman @ 2020-02-18  4:36 UTC (permalink / raw)
  To: kvm-ppc; +Cc: linuxppc-dev

In kvmppc_unmap_free_pte() in book3s_64_mmu_radix.c, we use the
non-constant value PTE_INDEX_SIZE to clear a PTE page.

We can instead use the constant RADIX_PTE_INDEX_SIZE, because we know
this code will only be running when the Radix MMU is active.

Note that we already use RADIX_PTE_INDEX_SIZE for the allocation of
kvm_pte_cache.

Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
 arch/powerpc/kvm/book3s_64_mmu_radix.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/kvm/book3s_64_mmu_radix.c b/arch/powerpc/kvm/book3s_64_mmu_radix.c
index 803940d79b73..134fbc1f029f 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_radix.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_radix.c
@@ -425,7 +425,7 @@ static void kvmppc_unmap_free_pte(struct kvm *kvm, pte_t *pte, bool full,
 				  unsigned int lpid)
 {
 	if (full) {
-		memset(pte, 0, sizeof(long) << PTE_INDEX_SIZE);
+		memset(pte, 0, sizeof(long) << RADIX_PTE_INDEX_SIZE);
 	} else {
 		pte_t *p = pte;
 		unsigned long it;
-- 
2.21.1


^ permalink raw reply related

* Re: [PATCH] powerpc/kprobes: Fix trap address when trap happened in real mode
From: Christophe Leroy @ 2020-02-18  5:58 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: linux-kernel, Anil S Keshavamurthy, Paul Mackerras, stable,
	Naveen N. Rao, linuxppc-dev, David S. Miller, Larry Finger
In-Reply-To: <20200218094421.6d402de389ce23a55a3ec084@kernel.org>



Le 18/02/2020 à 01:44, Masami Hiramatsu a écrit :
> On Mon, 17 Feb 2020 16:38:50 +0100
> Christophe Leroy <christophe.leroy@c-s.fr> wrote:
> 
>>
>>
>> Le 17/02/2020 à 11:27, Masami Hiramatsu a écrit :
>>> On Mon, 17 Feb 2020 10:03:22 +0100
>>> Christophe Leroy <christophe.leroy@c-s.fr> wrote:
>>>
>>>>
>>>>
>>>> Le 16/02/2020 à 13:34, Masami Hiramatsu a écrit :
>>>>> On Sat, 15 Feb 2020 11:28:49 +0100
>>>>> Christophe Leroy <christophe.leroy@c-s.fr> wrote:
>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> Le 14/02/2020 à 14:54, Masami Hiramatsu a écrit :
>>>>>>> Hi,
>>>>>>>
>>>>>>> On Fri, 14 Feb 2020 12:47:49 +0000 (UTC)
>>>>>>> Christophe Leroy <christophe.leroy@c-s.fr> wrote:
>>>>>>>
>>>>>>>> When a program check exception happens while MMU translation is
>>>>>>>> disabled, following Oops happens in kprobe_handler() in the following
>>>>>>>> test:
>>>>>>>>
>>>>>>>> 		} else if (*addr != BREAKPOINT_INSTRUCTION) {
>>>>>>>
>>>>>>> Thanks for the report and patch. I'm not so sure about powerpc implementation
>>>>>>> but at where the MMU translation is disabled, can the handler work correctly?
>>>>>>> (And where did you put the probe on?)
>>>>>>>
>>>>>>> Your fix may fix this Oops, but if the handler needs special care, it is an
>>>>>>> option to blacklist such place (if possible).
>>>>>>
>>>>>> I guess that's another story. Here we are not talking about a place
>>>>>> where kprobe has been illegitimately activated, but a place where there
>>>>>> is a valid trap, which generated a valid 'program check exception'. And
>>>>>> kprobe was off at that time.
>>>>>
>>>>> Ah, I got it. It is not a kprobe breakpoint, but to check that correctly,
>>>>> it has to know the address where the breakpoint happens. OK.
>>>>>
>>>>>>
>>>>>> As any 'program check exception' due to a trap (ie a BUG_ON, a WARN_ON,
>>>>>> a debugger breakpoint, a perf breakpoint, etc...) calls
>>>>>> kprobe_handler(), kprobe_handler() must be prepared to handle the case
>>>>>> where the MMU translation is disabled, even if probes are not supposed
>>>>>> to be set for functions running with MMU translation disabled.
>>>>>
>>>>> Can't we check the MMU is disabled there (as same as checking the exception
>>>>> happened in user space or not)?
>>>>>
>>>>
>>>> What do you mean by 'there' ? At the entry of kprobe_handler() ?
>>>>
>>>> That's what my patch does, it checks whether MMU is disabled or not. If
>>>> it is, it converts the address to a virtual address.
>>>>
>>>> Do you mean kprobe_handler() should bail out early as it does when the
>>>> trap happens in user mode ?
>>>
>>> Yes, that is what I meant.
>>>
>>>> Of course we can do that, I don't know
>>>> enough about kprobe to know if kprobe_handler() should manage events
>>>> that happened in real-mode or just ignore them. But I tested adding an
>>>> event on a function that runs in real-mode, and it (now) works.
>>>>
>>>> So, what should we do really ?
>>>
>>> I'm not sure how the powerpc kernel runs in real mode.
>>> But clearly, at least kprobe event can not handle that case because
>>> it tries to access memory by probe_kernel_read(). Unless that function
>>> correctly handles the address translation, I want to prohibit kprobes
>>> on such address.
>>>
>>> So what I would like to see is, something like below.
>>>
>>> diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c
>>> index 2d27ec4feee4..4771be152416 100644
>>> --- a/arch/powerpc/kernel/kprobes.c
>>> +++ b/arch/powerpc/kernel/kprobes.c
>>> @@ -261,7 +261,7 @@ int kprobe_handler(struct pt_regs *regs)
>>>           unsigned int *addr = (unsigned int *)regs->nip;
>>>           struct kprobe_ctlblk *kcb;
>>>    
>>> -       if (user_mode(regs))
>>> +       if (user_mode(regs) || !(regs->msr & MSR_IR))
>>>                   return 0;
>>>    
>>>           /*
>>>
>>>
>>
>> With this instead change of my patch, I get an Oops everytime a kprobe
>> event occurs in real-mode.
>>
>> This is because kprobe_handler() is now saying 'this trap doesn't belong
>> to me' for a trap that has been installed by it.
> 
> Hmm, on powerpc, kprobes is allowed to probe on the code which runs
> in the real mode? I think we should also prohibit it by blacklisting.
> (It is easy to add blacklist by NOKPROBE_SYMBOL(func))

Yes, I see a lot of them tagged with _ASM_NOKPROBE_SYMBOL() on PPC64, 
but none on PPC32. I suppose that's missing and have to be added. 
Nevertheless, if one symbol has been forgotten in the blacklist, I think 
it is a problem if it generate Oopses.

> Or, some parts are possble to run under both real mode and kernel mode?

I don't think so, at least on PPC32

> 
>>
>> So the 'program check' exception handler doesn't find the owner of the
>> trap hence generate an Oops.
>>
>> Even if we don't want kprobe() to proceed with the event entirely
>> (allthough it works at least for simple events), I'd expect it to fail
>> gracefully.
> 
> Agreed. I thought it was easy to identify real mode code. But if it is
> hard, we should apply your first patch and also skip user handlers
> if we are in the real mode (and increment missed count).

user handlers are already skipped.

What do you think about my latest proposal below ? If a trap is 
encoutered in real mode, if checks if the matching virtual address 
corresponds to a valid kprobe. If it is, it skips it. If not, it returns 
0 to tell "it's no me". You are also talking about incrementing the 
missed count. Who do we do that ?



@@ -264,6 +265,13 @@ int kprobe_handler(struct pt_regs *regs)
      if (user_mode(regs))
          return 0;

+    if (!(regs->msr & MSR_IR)) {
+        if (!get_kprobe(phys_to_virt(regs->nip)))
+            return 0;
+        regs->nip += 4;
+        return 1;
+    }
+
      /*
       * We don't want to be preempted for the entire
       * duration of kprobe processing


> 
> BTW, can the emulater handle the real mode code correctly?

I don't know, how do I test that ?

Christophe

^ permalink raw reply

* [PATCH v2 1/3] ASoC: fsl_asrc: Move common definition to fsl_asrc_common
From: Shengjiu Wang @ 2020-02-18  6:39 UTC (permalink / raw)
  To: timur, nicoleotsuka, Xiubo.Lee, festevam, broonie, alsa-devel,
	lgirdwood, perex, tiwai, robh+dt, mark.rutland, devicetree
  Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1582007379.git.shengjiu.wang@nxp.com>

There is a new ASRC included in i.MX serial platform, there
are some common definition can be shared with each other.
So move the common definition to a separate header file.

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
 sound/soc/fsl/fsl_asrc.h        | 11 +----------
 sound/soc/fsl/fsl_asrc_common.h | 21 +++++++++++++++++++++
 2 files changed, 22 insertions(+), 10 deletions(-)
 create mode 100644 sound/soc/fsl/fsl_asrc_common.h

diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
index 8a821132d9d0..e8abb27ffeda 100644
--- a/sound/soc/fsl/fsl_asrc.h
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -10,8 +10,7 @@
 #ifndef _FSL_ASRC_H
 #define _FSL_ASRC_H
 
-#define IN	0
-#define OUT	1
+#include  "fsl_asrc_common.h"
 
 #define ASRC_DMA_BUFFER_NUM		2
 #define ASRC_INPUTFIFO_THRESHOLD	32
@@ -283,14 +282,6 @@
 #define ASRMCR1i_OW16_MASK		(1 << ASRMCR1i_OW16_SHIFT)
 #define ASRMCR1i_OW16(v)		((v) << ASRMCR1i_OW16_SHIFT)
 
-
-enum asrc_pair_index {
-	ASRC_INVALID_PAIR = -1,
-	ASRC_PAIR_A = 0,
-	ASRC_PAIR_B = 1,
-	ASRC_PAIR_C = 2,
-};
-
 #define ASRC_PAIR_MAX_NUM	(ASRC_PAIR_C + 1)
 
 enum asrc_inclk {
diff --git a/sound/soc/fsl/fsl_asrc_common.h b/sound/soc/fsl/fsl_asrc_common.h
new file mode 100644
index 000000000000..8acc55778ff2
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc_common.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2019 NXP
+ *
+ */
+
+#ifndef _FSL_ASRC_COMMON_H
+#define _FSL_ASRC_COMMON_H
+
+/* directions */
+#define IN	0
+#define OUT	1
+
+enum asrc_pair_index {
+	ASRC_INVALID_PAIR = -1,
+	ASRC_PAIR_A = 0,
+	ASRC_PAIR_B = 1,
+	ASRC_PAIR_C = 2,
+};
+
+#endif /* _FSL_ASRC_COMMON_H */
-- 
2.21.0


^ permalink raw reply related

* [PATCH v2 0/3] Add new module driver for new ASRC
From: Shengjiu Wang @ 2020-02-18  6:39 UTC (permalink / raw)
  To: timur, nicoleotsuka, Xiubo.Lee, festevam, broonie, alsa-devel,
	lgirdwood, perex, tiwai, robh+dt, mark.rutland, devicetree
  Cc: linuxppc-dev, linux-kernel

Add new module driver for new ASRC in i.MX8MN.

Shengjiu Wang (3):
  ASoC: fsl_asrc: Move common definition to fsl_asrc_common
  ASoC: dt-bindings: fsl_easrc: Add document for EASRC
  ASoC: fsl_easrc: Add EASRC ASoC CPU DAI and platform drivers

changes in v2
- change i.MX815 to i.MX8MN
- Add changes in Kconfig and Makefile

 .../devicetree/bindings/sound/fsl,easrc.txt   |   57 +
 sound/soc/fsl/Kconfig                         |   10 +
 sound/soc/fsl/Makefile                        |    2 +
 sound/soc/fsl/fsl_asrc.h                      |   11 +-
 sound/soc/fsl/fsl_asrc_common.h               |   22 +
 sound/soc/fsl/fsl_easrc.c                     | 2265 +++++++++++++++++
 sound/soc/fsl/fsl_easrc.h                     |  668 +++++
 sound/soc/fsl/fsl_easrc_dma.c                 |  440 ++++
 8 files changed, 3465 insertions(+), 10 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/fsl,easrc.txt
 create mode 100644 sound/soc/fsl/fsl_asrc_common.h
 create mode 100644 sound/soc/fsl/fsl_easrc.c
 create mode 100644 sound/soc/fsl/fsl_easrc.h
 create mode 100644 sound/soc/fsl/fsl_easrc_dma.c

-- 
2.21.0


^ permalink raw reply

* [PATCH v2 2/3] ASoC: dt-bindings: fsl_easrc: Add document for EASRC
From: Shengjiu Wang @ 2020-02-18  6:39 UTC (permalink / raw)
  To: timur, nicoleotsuka, Xiubo.Lee, festevam, broonie, alsa-devel,
	lgirdwood, perex, tiwai, robh+dt, mark.rutland, devicetree
  Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1582007379.git.shengjiu.wang@nxp.com>

EASRC (Enhanced Asynchronous Sample Rate Converter) is a new
IP module found on i.MX8MN.

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
 .../devicetree/bindings/sound/fsl,easrc.txt   | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/fsl,easrc.txt

diff --git a/Documentation/devicetree/bindings/sound/fsl,easrc.txt b/Documentation/devicetree/bindings/sound/fsl,easrc.txt
new file mode 100644
index 000000000000..0e8153165e3b
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,easrc.txt
@@ -0,0 +1,57 @@
+NXP Asynchronous Sample Rate Converter (ASRC) Controller
+
+The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a
+signal associated with an input clock into a signal associated with a different
+output clock. The driver currently works as a Front End of DPCM with other Back
+Ends Audio controller such as ESAI, SSI and SAI. It has four context to support
+four substreams within totally 32 channels.
+
+Required properties:
+- compatible:                Contains "fsl,imx8mn-easrc".
+
+- reg:                       Offset and length of the register set for the
+			     device.
+
+- interrupts:                Contains the asrc interrupt.
+
+- dmas:                      Generic dma devicetree binding as described in
+		             Documentation/devicetree/bindings/dma/dma.txt.
+
+- dma-names:                 Contains "ctx0_rx", "ctx0_tx",
+				      "ctx1_rx", "ctx1_tx",
+			              "ctx2_rx", "ctx2_tx",
+				      "ctx3_rx", "ctx3_tx".
+
+- clocks:                    Contains an entry for each entry in clock-names.
+
+- clock-names:               "mem" - Peripheral clock to driver module.
+
+- fsl,easrc-ram-script-name: The coefficient table for the filters
+
+- fsl,asrc-rate:             Defines a mutual sample rate used by DPCM Back
+			     Ends.
+
+- fsl,asrc-width:            Defines a mutual sample width used by DPCM Back
+			     Ends.
+
+Example:
+
+easrc: easrc@300C0000 {
+	compatible = "fsl,imx8mn-easrc";
+	reg = <0x0 0x300C0000 0x0 0x10000>;
+	interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&clk IMX8MN_CLK_ASRC_ROOT>;
+	clock-names = "mem";
+	dmas = <&sdma2 16 23 0> , <&sdma2 17 23 0>,
+	       <&sdma2 18 23 0> , <&sdma2 19 23 0>,
+	       <&sdma2 20 23 0> , <&sdma2 21 23 0>,
+	       <&sdma2 22 23 0> , <&sdma2 23 23 0>;
+	dma-names = "ctx0_rx", "ctx0_tx",
+		    "ctx1_rx", "ctx1_tx",
+		    "ctx2_rx", "ctx2_tx",
+		    "ctx3_rx", "ctx3_tx";
+	fsl,easrc-ram-script-name = "imx/easrc/easrc-imx8mn.bin";
+	fsl,asrc-rate  = <8000>;
+	fsl,asrc-width = <16>;
+	status = "disabled";
+};
-- 
2.21.0


^ permalink raw reply related

* [PATCH v2 3/3] ASoC: fsl_easrc: Add EASRC ASoC CPU DAI and platform drivers
From: Shengjiu Wang @ 2020-02-18  6:39 UTC (permalink / raw)
  To: timur, nicoleotsuka, Xiubo.Lee, festevam, broonie, alsa-devel,
	lgirdwood, perex, tiwai, robh+dt, mark.rutland, devicetree
  Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1582007379.git.shengjiu.wang@nxp.com>

EASRC (Enhanced Asynchronous Sample Rate Converter) is a new IP module
found on i.MX8MN. It is different with old ASRC module.

The primary features for the EASRC are as follows:
- 4 Contexts - groups of channels with an independent time base
- Fully independent and concurrent context control
- Simultaneous processing of up to 32 audio channels
- Programmable filter charachteristics for each context
- 32, 24, 20, and 16-bit fixed point audio sample support
- 32-bit floating point audio sample support
- 8kHz to 384kHz sample rate
- 1/16 to 8x sample rate conversion ratio

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
 sound/soc/fsl/Kconfig           |   10 +
 sound/soc/fsl/Makefile          |    2 +
 sound/soc/fsl/fsl_asrc_common.h |    1 +
 sound/soc/fsl/fsl_easrc.c       | 2265 +++++++++++++++++++++++++++++++
 sound/soc/fsl/fsl_easrc.h       |  668 +++++++++
 sound/soc/fsl/fsl_easrc_dma.c   |  440 ++++++
 6 files changed, 3386 insertions(+)
 create mode 100644 sound/soc/fsl/fsl_easrc.c
 create mode 100644 sound/soc/fsl/fsl_easrc.h
 create mode 100644 sound/soc/fsl/fsl_easrc_dma.c

diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 65e8cd4be930..21ba7642b5d0 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -84,6 +84,16 @@ config SND_SOC_FSL_MICFIL
 	  Say Y if you want to add Pulse Density Modulation microphone
 	  interface (MICFIL) support for NXP.
 
+config SND_SOC_FSL_EASRC
+	tristate "Enhanced Asynchronous Sample Rate Converter (EASRC) module support"
+	select REGMAP_MMIO
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	help
+	  Say Y if you want to add Enhanced ASRC support for NXP. The ASRC is
+	  a digital module that converts audio from a source sample rate to a
+	  destination sample rate. It is a new design module compare with the
+	  old ASRC.
+
 config SND_SOC_FSL_UTILS
 	tristate
 
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 8cde88c72d93..a5a2d08930f4 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -24,6 +24,7 @@ snd-soc-fsl-micfil-objs := fsl_micfil.o
 snd-soc-fsl-utils-objs := fsl_utils.o
 snd-soc-fsl-dma-objs := fsl_dma.o
 snd-soc-fsl-mqs-objs := fsl_mqs.o
+snd-soc-fsl-easrc-objs := fsl_easrc.o fsl_easrc_dma.o
 
 obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
 obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
@@ -35,6 +36,7 @@ obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o
 obj-$(CONFIG_SND_SOC_FSL_MICFIL) += snd-soc-fsl-micfil.o
 obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
 obj-$(CONFIG_SND_SOC_FSL_MQS) += snd-soc-fsl-mqs.o
+obj-$(CONFIG_SND_SOC_FSL_EASRC) += snd-soc-fsl-easrc.o
 obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
 
 # MPC5200 Platform Support
diff --git a/sound/soc/fsl/fsl_asrc_common.h b/sound/soc/fsl/fsl_asrc_common.h
index 8acc55778ff2..c2056b661f15 100644
--- a/sound/soc/fsl/fsl_asrc_common.h
+++ b/sound/soc/fsl/fsl_asrc_common.h
@@ -16,6 +16,7 @@ enum asrc_pair_index {
 	ASRC_PAIR_A = 0,
 	ASRC_PAIR_B = 1,
 	ASRC_PAIR_C = 2,
+	ASRC_PAIR_D = 3,
 };
 
 #endif /* _FSL_ASRC_COMMON_H */
diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c
new file mode 100644
index 000000000000..6fe2953317f2
--- /dev/null
+++ b/sound/soc/fsl/fsl_easrc.c
@@ -0,0 +1,2265 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2019 NXP
+
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/kobject.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/sched/signal.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/gcd.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+
+#include "fsl_easrc.h"
+#include "imx-pcm.h"
+
+#define FSL_EASRC_FORMATS       (SNDRV_PCM_FMTBIT_S16_LE | \
+				 SNDRV_PCM_FMTBIT_U16_LE | \
+				 SNDRV_PCM_FMTBIT_S24_LE | \
+				 SNDRV_PCM_FMTBIT_S24_3LE | \
+				 SNDRV_PCM_FMTBIT_U24_LE | \
+				 SNDRV_PCM_FMTBIT_U24_3LE | \
+				 SNDRV_PCM_FMTBIT_S32_LE | \
+				 SNDRV_PCM_FMTBIT_U32_LE | \
+				 SNDRV_PCM_FMTBIT_S20_3LE | \
+				 SNDRV_PCM_FMTBIT_U20_3LE | \
+				 SNDRV_PCM_FMTBIT_FLOAT_LE)
+
+static int fsl_easrc_iec958_put_bits(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+	struct fsl_easrc *easrc = snd_soc_component_get_drvdata(comp);
+	struct soc_mreg_control *mc =
+		(struct soc_mreg_control *)kcontrol->private_value;
+	unsigned int regval = ucontrol->value.integer.value[0];
+
+	easrc->bps_iec958[mc->regbase] = regval;
+
+	return 0;
+}
+
+static int fsl_easrc_iec958_get_bits(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+	struct fsl_easrc *easrc = snd_soc_component_get_drvdata(comp);
+	struct soc_mreg_control *mc =
+		(struct soc_mreg_control *)kcontrol->private_value;
+
+	ucontrol->value.enumerated.item[0] = easrc->bps_iec958[mc->regbase];
+
+	return 0;
+}
+
+int fsl_easrc_get_reg(struct snd_kcontrol *kcontrol,
+		      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_mreg_control *mc =
+		(struct soc_mreg_control *)kcontrol->private_value;
+	unsigned int regval;
+	int ret;
+
+	ret = snd_soc_component_read(component, mc->regbase, &regval);
+	if (ret < 0)
+		return ret;
+
+	ucontrol->value.integer.value[0] = regval;
+
+	return 0;
+}
+
+int fsl_easrc_set_reg(struct snd_kcontrol *kcontrol,
+		      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_mreg_control *mc =
+		(struct soc_mreg_control *)kcontrol->private_value;
+	unsigned int regval = ucontrol->value.integer.value[0];
+	int ret;
+
+	ret = snd_soc_component_write(component, mc->regbase, regval);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+#define SOC_SINGLE_REG_RW(xname, xreg) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+	.info = snd_soc_info_xr_sx, .get = fsl_easrc_get_reg, \
+	.put = fsl_easrc_set_reg, \
+	.private_value = (unsigned long)&(struct soc_mreg_control) \
+		{ .regbase = xreg, .regcount = 1, .nbits = 32, \
+		  .invert = 0, .min = 0, .max = 0xffffffff, } }
+
+#define SOC_SINGLE_VAL_RW(xname, xreg) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+	.info = snd_soc_info_xr_sx, .get = fsl_easrc_iec958_get_bits, \
+	.put = fsl_easrc_iec958_put_bits, \
+	.private_value = (unsigned long)&(struct soc_mreg_control) \
+		{ .regbase = xreg, .regcount = 1, .nbits = 32, \
+		  .invert = 0, .min = 0, .max = 2, } }
+
+static const struct snd_kcontrol_new fsl_easrc_snd_controls[] = {
+	SOC_SINGLE("Context 0 Dither Switch", REG_EASRC_COC(0), 0, 1, 0),
+	SOC_SINGLE("Context 1 Dither Switch", REG_EASRC_COC(1), 0, 1, 0),
+	SOC_SINGLE("Context 2 Dither Switch", REG_EASRC_COC(2), 0, 1, 0),
+	SOC_SINGLE("Context 3 Dither Switch", REG_EASRC_COC(3), 0, 1, 0),
+
+	SOC_SINGLE("Context 0 IEC958 Validity", REG_EASRC_COC(0), 2, 1, 0),
+	SOC_SINGLE("Context 1 IEC958 Validity", REG_EASRC_COC(1), 2, 1, 0),
+	SOC_SINGLE("Context 2 IEC958 Validity", REG_EASRC_COC(2), 2, 1, 0),
+	SOC_SINGLE("Context 3 IEC958 Validity", REG_EASRC_COC(3), 2, 1, 0),
+
+	SOC_SINGLE_VAL_RW("Context 0 IEC958 Bits Per Sample", 0),
+	SOC_SINGLE_VAL_RW("Context 1 IEC958 Bits Per Sample", 1),
+	SOC_SINGLE_VAL_RW("Context 2 IEC958 Bits Per Sample", 2),
+	SOC_SINGLE_VAL_RW("Context 3 IEC958 Bits Per Sample", 3),
+
+	SOC_SINGLE_REG_RW("Context 0 IEC958 CS0", REG_EASRC_CS0(0)),
+	SOC_SINGLE_REG_RW("Context 1 IEC958 CS0", REG_EASRC_CS0(1)),
+	SOC_SINGLE_REG_RW("Context 2 IEC958 CS0", REG_EASRC_CS0(2)),
+	SOC_SINGLE_REG_RW("Context 3 IEC958 CS0", REG_EASRC_CS0(3)),
+	SOC_SINGLE_REG_RW("Context 0 IEC958 CS1", REG_EASRC_CS1(0)),
+	SOC_SINGLE_REG_RW("Context 1 IEC958 CS1", REG_EASRC_CS1(1)),
+	SOC_SINGLE_REG_RW("Context 2 IEC958 CS1", REG_EASRC_CS1(2)),
+	SOC_SINGLE_REG_RW("Context 3 IEC958 CS1", REG_EASRC_CS1(3)),
+	SOC_SINGLE_REG_RW("Context 0 IEC958 CS2", REG_EASRC_CS2(0)),
+	SOC_SINGLE_REG_RW("Context 1 IEC958 CS2", REG_EASRC_CS2(1)),
+	SOC_SINGLE_REG_RW("Context 2 IEC958 CS2", REG_EASRC_CS2(2)),
+	SOC_SINGLE_REG_RW("Context 3 IEC958 CS2", REG_EASRC_CS2(3)),
+	SOC_SINGLE_REG_RW("Context 0 IEC958 CS3", REG_EASRC_CS3(0)),
+	SOC_SINGLE_REG_RW("Context 1 IEC958 CS3", REG_EASRC_CS3(1)),
+	SOC_SINGLE_REG_RW("Context 2 IEC958 CS3", REG_EASRC_CS3(2)),
+	SOC_SINGLE_REG_RW("Context 3 IEC958 CS3", REG_EASRC_CS3(3)),
+	SOC_SINGLE_REG_RW("Context 0 IEC958 CS4", REG_EASRC_CS4(0)),
+	SOC_SINGLE_REG_RW("Context 1 IEC958 CS4", REG_EASRC_CS4(1)),
+	SOC_SINGLE_REG_RW("Context 2 IEC958 CS4", REG_EASRC_CS4(2)),
+	SOC_SINGLE_REG_RW("Context 3 IEC958 CS4", REG_EASRC_CS4(3)),
+	SOC_SINGLE_REG_RW("Context 0 IEC958 CS5", REG_EASRC_CS5(0)),
+	SOC_SINGLE_REG_RW("Context 1 IEC958 CS5", REG_EASRC_CS5(1)),
+	SOC_SINGLE_REG_RW("Context 2 IEC958 CS5", REG_EASRC_CS5(2)),
+	SOC_SINGLE_REG_RW("Context 3 IEC958 CS5", REG_EASRC_CS5(3)),
+};
+
+/* set_rs_ratio
+ *
+ * According to the resample taps, calculate the resample ratio
+ */
+static int set_rs_ratio(struct fsl_easrc_context *ctx)
+{
+	struct fsl_easrc *easrc = ctx->easrc;
+	unsigned int in_rate = ctx->in_params.norm_rate;
+	unsigned int out_rate = ctx->out_params.norm_rate;
+	unsigned int int_bits;
+	unsigned int frac_bits;
+	u64 val;
+	u32 *r;
+
+	switch (easrc->rs_num_taps) {
+	case EASRC_RS_32_TAPS:
+		int_bits = 5;
+		frac_bits = 39;
+		break;
+	case EASRC_RS_64_TAPS:
+		int_bits = 6;
+		frac_bits = 38;
+		break;
+	case EASRC_RS_128_TAPS:
+		int_bits = 7;
+		frac_bits = 37;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	val = (u64)in_rate << frac_bits;
+	do_div(val, out_rate);
+	r = (uint32_t *)&val;
+	regmap_write(easrc->regmap, REG_EASRC_RRL(ctx->index),
+		     EASRC_RRL_RS_RL(r[0]));
+	regmap_write(easrc->regmap, REG_EASRC_RRH(ctx->index),
+		     EASRC_RRH_RS_RH(r[1]));
+
+	return 0;
+}
+
+/* normalize input and output sample rates */
+static void fsl_easrc_normalize_rates(struct fsl_easrc_context *ctx)
+{
+	int a, b;
+
+	if (!ctx)
+		return;
+
+	a = ctx->in_params.sample_rate;
+	b = ctx->out_params.sample_rate;
+
+	a = gcd(a, b);
+
+	/* divide by gcd to normalize the rate */
+	ctx->in_params.norm_rate = ctx->in_params.sample_rate / a;
+	ctx->out_params.norm_rate = ctx->out_params.sample_rate / a;
+}
+
+/* resets the pointer of the coeff memory pointers */
+static int fsl_coeff_mem_ptr_reset(struct fsl_easrc *easrc,
+				   unsigned int ctx_id, int mem_type)
+{
+	struct device *dev;
+	u32 reg, mask, val;
+
+	if (!easrc)
+		return -ENODEV;
+
+	dev = &easrc->pdev->dev;
+
+	switch (mem_type) {
+	case EASRC_PF_COEFF_MEM:
+		/* This resets the prefilter memory pointer addr */
+		if (ctx_id >= EASRC_CTX_MAX_NUM) {
+			dev_err(dev, "Invalid context id[%d]\n", ctx_id);
+			return -EINVAL;
+		}
+
+		reg = REG_EASRC_CCE1(ctx_id);
+		mask = EASRC_CCE1_COEF_MEM_RST_MASK;
+		val = EASRC_CCE1_COEF_MEM_RST;
+		break;
+	case EASRC_RS_COEFF_MEM:
+		/* This resets the resampling memory pointer addr */
+		reg = REG_EASRC_CRCC;
+		mask = EASRC_CRCC_RS_CPR_MASK;
+		val = EASRC_CRCC_RS_CPR;
+		break;
+	default:
+		dev_err(dev, "Unknown memory type\n");
+		return -EINVAL;
+	}
+
+	/*  To reset the write pointer back to zero, the register field
+	 *  ASRC_CTX_CTRL_EXT1x[PF_COEFF_MEM_RST] can be toggled from
+	 *  0x0 to 0x1 to 0x0.
+	 */
+	regmap_update_bits(easrc->regmap, reg, mask, 0);
+	regmap_update_bits(easrc->regmap, reg, mask, val);
+	regmap_update_bits(easrc->regmap, reg, mask, 0);
+
+	return 0;
+}
+
+static inline uint32_t bits_taps_to_val(unsigned int t)
+{
+	switch (t) {
+	case EASRC_RS_32_TAPS:
+		return 32;
+	case EASRC_RS_64_TAPS:
+		return 64;
+	case EASRC_RS_128_TAPS:
+		return 128;
+	}
+
+	return 0;
+}
+
+static int fsl_easrc_resampler_config(struct fsl_easrc *easrc)
+{
+	struct device *dev = &easrc->pdev->dev;
+	struct asrc_firmware_hdr *hdr =  easrc->firmware_hdr;
+	struct interp_params *interp = easrc->interp;
+	struct interp_params *selected_interp = NULL;
+	unsigned int num_coeff;
+	unsigned int i;
+	u64 *arr;
+	u32 *r;
+	int ret;
+
+	if (!hdr) {
+		dev_err(dev, "firmware not loaded!\n");
+		return -ENODEV;
+	}
+
+	for (i = 0; i < hdr->interp_scen; i++) {
+		if ((interp[i].num_taps - 1) ==
+				bits_taps_to_val(easrc->rs_num_taps)) {
+			arr = interp[i].coeff;
+			selected_interp = &interp[i];
+			dev_dbg(dev, "Selected interp_filter: %u taps - %u phases\n",
+				selected_interp->num_taps,
+				selected_interp->num_phases);
+			break;
+		}
+	}
+
+	if (!selected_interp) {
+		dev_err(dev, "failed to get interpreter configuration\n");
+		return -EINVAL;
+	}
+
+	/*
+	 *  RS_LOW - first half of center tap of the sinc function
+	 *  RS_HIGH - second half of center tap of the sinc function
+	 *  This is due to the fact the resampling function must be
+	 *  symetrical - i.e. odd number of taps
+	 */
+	r = (uint32_t *)&selected_interp->center_tap;
+	regmap_write(easrc->regmap, REG_EASRC_RCTCL, EASRC_RCTCL_RS_CL(r[0]));
+	regmap_write(easrc->regmap, REG_EASRC_RCTCH, EASRC_RCTCH_RS_CH(r[1]));
+
+	/* Write Number of Resampling Coefficient Taps
+	 * 00b - 32-Tap Resampling Filter
+	 * 01b - 64-Tap Resampling Filter
+	 * 10b - 128-Tap Resampling Filter
+	 * 11b - N/A
+	 */
+	regmap_update_bits(easrc->regmap, REG_EASRC_CRCC,
+			   EASRC_CRCC_RS_TAPS_MASK,
+			   EASRC_CRCC_RS_TAPS(easrc->rs_num_taps));
+
+	/* Reset prefilter coefficient pointer back to 0 */
+	ret = fsl_coeff_mem_ptr_reset(easrc, 0, EASRC_RS_COEFF_MEM);
+	if (ret)
+		return ret;
+
+	/* When the filter is programmed to run in:
+	 * 32-tap mode, 16-taps, 128-phases 4-coefficients per phase
+	 * 64-tap mode, 32-taps, 64-phases 4-coefficients per phase
+	 * 128-tap mode, 64-taps, 32-phases 4-coefficients per phase
+	 * This means the number of writes is constant no matter
+	 * the mode we are using
+	 */
+	num_coeff = 16 * 128 * 4;
+
+	for (i = 0; i < num_coeff; i++) {
+		r = (uint32_t *)&arr[i];
+		regmap_write(easrc->regmap, REG_EASRC_CRCM,
+			     EASRC_CRCM_RS_CWD(r[0]));
+		regmap_write(easrc->regmap, REG_EASRC_CRCM,
+			     EASRC_CRCM_RS_CWD(r[1]));
+	}
+
+	return 0;
+}
+
+/*****************************************************************************
+ *  Scale filter coefficients (64 bits float)
+ *  For input float32 normalized range (1.0,-1.0) -> output int[16,24,32]:
+ *      scale it by multiplying filter coefficients by 2^31
+ *  For input int[16, 24, 32] -> output float32
+ *      scale it by multiplying filter coefficients by 2^-15, 2^-23, 2^-31
+ *  input:
+ *      easrc:  Structure pointer of fsl_easrc
+ *      infilter : Pointer to non-scaled input filter
+ *      shift:  The multiply factor
+ *  output:
+ *      outfilter: scaled filter
+ *****************************************************************************/
+static int NormalizedFilterForFloat32InIntOut(struct fsl_easrc *easrc,
+					      u64 *infilter,
+					      u64 *outfilter,
+					      int shift)
+{
+	struct device *dev = &easrc->pdev->dev;
+	u64 coef = *infilter;
+	s64 exp  = (coef & 0x7ff0000000000000ll) >> 52;
+	u64 outcoef;
+
+	/*
+	 * If exponent is zero (value == 0), or 7ff (value == NaNs)
+	 * dont touch the content
+	 */
+	if (((coef & 0x7ff0000000000000ll) == 0) ||
+	    ((coef & 0x7ff0000000000000ll) == ((u64)0x7ff << 52))) {
+		*outfilter = coef;
+	} else {
+		if ((shift > 0 && (shift + exp) >= 2047) ||
+		    (shift < 0 && (exp + shift) <= 0)) {
+			dev_err(dev, "coef error\n");
+			return -EINVAL;
+		}
+
+		/* coefficient * 2^shift ==>  coefficient_exp + shift */
+		exp += shift;
+		outcoef = (u64)(coef & 0x800FFFFFFFFFFFFFll) +
+					((u64)exp << 52);
+		*outfilter = outcoef;
+	}
+
+	return 0;
+}
+
+static int write_pf_coeff_mem(struct fsl_easrc *easrc, int ctx_id,
+			      u64 *arr, int n_taps, int shift)
+{
+	struct device *dev = &easrc->pdev->dev;
+	int ret = 0;
+	int i;
+	u32 *r;
+	u64 tmp;
+
+	/* If STx_NUM_TAPS is set to 0x0 then return */
+	if (!n_taps)
+		return 0;
+
+	if (!arr) {
+		dev_err(dev, "NULL buffer\n");
+		return -EINVAL;
+	}
+
+	/* When switching between stages, the address pointer
+	 * should be reset back to 0x0 before performing a write
+	 */
+	ret = fsl_coeff_mem_ptr_reset(easrc, ctx_id, EASRC_PF_COEFF_MEM);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < (n_taps + 1) / 2; i++) {
+		ret = NormalizedFilterForFloat32InIntOut(easrc, &arr[i], &tmp,
+							 shift);
+		if (ret)
+			return ret;
+
+		r = (uint32_t *)&tmp;
+		regmap_write(easrc->regmap, REG_EASRC_PCF(ctx_id),
+			     EASRC_PCF_CD(r[0]));
+		regmap_write(easrc->regmap, REG_EASRC_PCF(ctx_id),
+			     EASRC_PCF_CD(r[1]));
+	}
+
+	return 0;
+}
+
+static int fsl_easrc_prefilter_config(struct fsl_easrc *easrc,
+				      unsigned int ctx_id)
+{
+	struct fsl_easrc_context *ctx;
+	struct asrc_firmware_hdr *hdr;
+	struct prefil_params *prefil, *selected_prefil = NULL;
+	struct device *dev;
+	u32 inrate, outrate, offset = 0;
+	int ret, i;
+
+	/* to modify prefilter coeficients, the user must perform
+	 * a write in ASRC_PRE_COEFF_FIFO[COEFF_DATA] while the
+	 * RUN_EN for that context is set to 0
+	 */
+	if (!easrc)
+		return -ENODEV;
+
+	dev = &easrc->pdev->dev;
+
+	if (ctx_id >= EASRC_CTX_MAX_NUM) {
+		dev_err(dev, "Invalid context id[%d]\n", ctx_id);
+		return -EINVAL;
+	}
+
+	ctx = easrc->ctx[ctx_id];
+
+	ctx->in_filled_sample = bits_taps_to_val(easrc->rs_num_taps) / 2;
+	ctx->out_missed_sample = ctx->in_filled_sample *
+				 ctx->out_params.sample_rate /
+				 ctx->in_params.sample_rate;
+
+	ctx->st1_num_taps = 0;
+	ctx->st2_num_taps = 0;
+
+	regmap_write(easrc->regmap, REG_EASRC_CCE1(ctx_id), 0);
+	regmap_write(easrc->regmap, REG_EASRC_CCE2(ctx_id), 0);
+
+	/* prefilter is enabled only when doing downsampling.
+	 * When out_rate >= in_rate, pf will be in bypass mode
+	 */
+	if (ctx->out_params.sample_rate >= ctx->in_params.sample_rate) {
+		if (ctx->out_params.sample_rate == ctx->in_params.sample_rate)
+			regmap_update_bits(easrc->regmap,
+					   REG_EASRC_CCE1(ctx_id),
+					   EASRC_CCE1_RS_BYPASS_MASK,
+					   EASRC_CCE1_RS_BYPASS);
+
+		if (ctx->in_params.sample_format == SNDRV_PCM_FORMAT_FLOAT_LE &&
+		    ctx->out_params.sample_format != SNDRV_PCM_FORMAT_FLOAT_LE) {
+			ctx->st1_num_taps = 1;
+			ctx->st1_coeff    = &easrc->const_coeff;
+			ctx->st1_num_exp  = 1;
+			ctx->st2_num_taps = 0;
+			ctx->st1_addexp = 31;
+		} else if (ctx->in_params.sample_format != SNDRV_PCM_FORMAT_FLOAT_LE &&
+			   ctx->out_params.sample_format == SNDRV_PCM_FORMAT_FLOAT_LE) {
+			ctx->st1_num_taps = 1;
+			ctx->st1_coeff    = &easrc->const_coeff;
+			ctx->st1_num_exp  = 1;
+			ctx->st2_num_taps = 0;
+			ctx->st1_addexp -= ctx->in_params.fmt.addexp;
+		} else {
+			ctx->st1_num_taps = 1;
+			ctx->st1_coeff    = &easrc->const_coeff;
+			ctx->st1_num_exp  = 1;
+			ctx->st2_num_taps = 0;
+		}
+	} else {
+		inrate = ctx->in_params.norm_rate;
+		outrate = ctx->out_params.norm_rate;
+
+		hdr = easrc->firmware_hdr;
+		prefil = easrc->prefil;
+
+		for (i = 0; i < hdr->prefil_scen; i++) {
+			if (inrate == prefil[i].insr && outrate == prefil[i].outsr) {
+				selected_prefil = &prefil[i];
+				dev_dbg(dev, "Selected prefilter: %u insr, %u outsr, %u st1_taps, %u st2_taps\n",
+					selected_prefil->insr,
+					selected_prefil->outsr,
+					selected_prefil->st1_taps,
+					selected_prefil->st2_taps);
+				break;
+			}
+		}
+
+		if (!selected_prefil) {
+			dev_err(dev, "Conversion from in ratio %u(%u) to out ratio %u(%u) is not supported\n",
+				ctx->in_params.sample_rate,
+				inrate,
+				ctx->out_params.sample_rate, outrate);
+			return -EINVAL;
+		}
+
+		/* in prefilter coeff array, first st1_num_taps represent the
+		 * stage1 prefilter coefficients followed by next st2_num_taps
+		 * representing stage 2 coefficients
+		 */
+		ctx->st1_num_taps = selected_prefil->st1_taps;
+		ctx->st1_coeff    = selected_prefil->coeff;
+		ctx->st1_num_exp  = selected_prefil->st1_exp;
+
+		offset = ((selected_prefil->st1_taps + 1) / 2);
+		ctx->st2_num_taps = selected_prefil->st2_taps;
+		ctx->st2_coeff    = selected_prefil->coeff + offset;
+
+		if (ctx->in_params.sample_format == SNDRV_PCM_FORMAT_FLOAT_LE &&
+		    ctx->out_params.sample_format != SNDRV_PCM_FORMAT_FLOAT_LE) {
+			/* only change stage2 coefficient for 2 stage case */
+			if (ctx->st2_num_taps > 0)
+				ctx->st2_addexp = 31;
+			else
+				ctx->st1_addexp = 31;
+		} else if (ctx->in_params.sample_format != SNDRV_PCM_FORMAT_FLOAT_LE &&
+			   ctx->out_params.sample_format == SNDRV_PCM_FORMAT_FLOAT_LE) {
+			if (ctx->st2_num_taps > 0)
+				ctx->st2_addexp -= ctx->in_params.fmt.addexp;
+			else
+				ctx->st1_addexp -= ctx->in_params.fmt.addexp;
+		}
+	}
+
+	ctx->in_filled_sample += (ctx->st1_num_taps / 2) * ctx->st1_num_exp +
+				  ctx->st2_num_taps / 2;
+	ctx->out_missed_sample = ctx->in_filled_sample *
+				 ctx->out_params.sample_rate /
+				 ctx->in_params.sample_rate;
+
+	if (ctx->in_filled_sample * ctx->out_params.sample_rate %
+				ctx->in_params.sample_rate != 0)
+		ctx->out_missed_sample += 1;
+	/* To modify the value of a prefilter coefficient, the user must
+	 * perform a write to the register ASRC_PRE_COEFF_FIFOn[COEFF_DATA]
+	 * while the respective context RUN_EN bit is set to 0b0
+	 */
+	regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx_id),
+			   EASRC_CC_EN_MASK, 0);
+
+	if (ctx->st1_num_taps > EASRC_MAX_PF_TAPS) {
+		dev_err(dev, "ST1 taps [%d] mus be lower than %d\n",
+			ctx->st1_num_taps, EASRC_MAX_PF_TAPS);
+		ret = -EINVAL;
+		goto ctx_error;
+	}
+
+	/* Update ctx ST1_NUM_TAPS in Context Control Extended 2 register */
+	regmap_update_bits(easrc->regmap, REG_EASRC_CCE2(ctx_id),
+			   EASRC_CCE2_ST1_TAPS_MASK,
+			   EASRC_CCE2_ST1_TAPS(ctx->st1_num_taps - 1));
+
+	/* Prefilter Coefficient Write Select to write in ST1 coeff */
+	regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+			   EASRC_CCE1_COEF_WS_MASK,
+			   EASRC_PF_ST1_COEFF_WR << EASRC_CCE1_COEF_WS_SHIFT);
+
+	ret = write_pf_coeff_mem(easrc, ctx_id,
+				 ctx->st1_coeff,
+				 ctx->st1_num_taps,
+				 ctx->st1_addexp);
+	if (ret)
+		goto ctx_error;
+
+	if (ctx->st2_num_taps > 0) {
+		if (ctx->st2_num_taps + ctx->st1_num_taps > EASRC_MAX_PF_TAPS) {
+			dev_err(dev, "ST2 taps [%d] mus be lower than %d\n",
+				ctx->st2_num_taps, EASRC_MAX_PF_TAPS);
+			ret = -EINVAL;
+			goto ctx_error;
+		}
+
+		regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+				   EASRC_CCE1_PF_TSEN_MASK,
+				   EASRC_CCE1_PF_TSEN);
+		/*
+		 * Enable prefilter stage1 writeback floating point
+		 * which is used for FLOAT_LE case
+		 */
+		regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+				   EASRC_CCE1_PF_ST1_WBFP_MASK,
+				   EASRC_CCE1_PF_ST1_WBFP);
+
+		regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+				   EASRC_CCE1_PF_EXP_MASK,
+				   EASRC_CCE1_PF_EXP(ctx->st1_num_exp - 1));
+
+		/* Update ctx ST2_NUM_TAPS in Context Control Extended 2 reg */
+		regmap_update_bits(easrc->regmap, REG_EASRC_CCE2(ctx_id),
+				   EASRC_CCE2_ST2_TAPS_MASK,
+				   EASRC_CCE2_ST2_TAPS(ctx->st2_num_taps - 1));
+
+		/* Prefilter Coefficient Write Select to write in ST2 coeff */
+		regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+				   EASRC_CCE1_COEF_WS_MASK,
+				   EASRC_PF_ST2_COEFF_WR << EASRC_CCE1_COEF_WS_SHIFT);
+
+		ret = write_pf_coeff_mem(easrc, ctx_id,
+					 ctx->st2_coeff,
+					 ctx->st2_num_taps,
+					 ctx->st2_addexp);
+		if (ret)
+			goto ctx_error;
+	}
+
+	return 0;
+
+ctx_error:
+	return ret;
+}
+
+static int fsl_easrc_max_ch_for_slot(struct fsl_easrc_context *ctx,
+				     struct fsl_easrc_slot *slot)
+{
+	int st1_mem_alloc = 0, st2_mem_alloc = 0;
+	int pf_mem_alloc = 0;
+	int max_channels = 8 - slot->num_channel;
+	int channels = 0;
+
+	if (ctx->st1_num_taps > 0) {
+		if (ctx->st2_num_taps > 0)
+			st1_mem_alloc =
+				(ctx->st1_num_taps - 1) * ctx->st1_num_exp + 1;
+		else
+			st1_mem_alloc = ctx->st1_num_taps;
+	}
+
+	if (ctx->st2_num_taps > 0)
+		st2_mem_alloc = ctx->st2_num_taps;
+
+	pf_mem_alloc = st1_mem_alloc + st2_mem_alloc;
+
+	if (pf_mem_alloc != 0)
+		channels = (6144 - slot->pf_mem_used) / pf_mem_alloc;
+	else
+		channels = 8;
+
+	if (channels < max_channels)
+		max_channels = channels;
+
+	return max_channels;
+}
+
+static int fsl_easrc_config_one_slot(struct fsl_easrc_context *ctx,
+				     struct fsl_easrc_slot *slot,
+				     unsigned int slot_idx,
+				     unsigned int reg0,
+				     unsigned int reg1,
+				     unsigned int reg2,
+				     unsigned int reg3,
+				     unsigned int *req_channels,
+				     unsigned int *start_channel,
+				     unsigned int *avail_channel)
+{
+	struct fsl_easrc *easrc = ctx->easrc;
+	int st1_chanxexp, st1_mem_alloc = 0, st2_mem_alloc = 0;
+	unsigned int addr;
+
+	if (*req_channels <= *avail_channel) {
+		slot->num_channel = *req_channels;
+		slot->min_channel = *start_channel;
+		slot->max_channel = *start_channel + *req_channels - 1;
+		slot->ctx_index = ctx->index;
+		slot->busy = true;
+		*start_channel += *req_channels;
+		*req_channels = 0;
+	} else {
+		slot->num_channel = *avail_channel;
+		slot->min_channel = *start_channel;
+		slot->max_channel = *start_channel + *avail_channel - 1;
+		slot->ctx_index = ctx->index;
+		slot->busy = true;
+		*start_channel += *avail_channel;
+		*req_channels -= *avail_channel;
+	}
+
+	regmap_update_bits(easrc->regmap, reg0,
+			   EASRC_DPCS0R0_MAXCH_MASK,
+			   EASRC_DPCS0R0_MAXCH(slot->max_channel));
+
+	regmap_update_bits(easrc->regmap, reg0,
+			   EASRC_DPCS0R0_MINCH_MASK,
+			   EASRC_DPCS0R0_MINCH(slot->min_channel));
+
+	regmap_update_bits(easrc->regmap, reg0,
+			   EASRC_DPCS0R0_NUMCH_MASK,
+			   EASRC_DPCS0R0_NUMCH(slot->num_channel - 1));
+
+	regmap_update_bits(easrc->regmap, reg0,
+			   EASRC_DPCS0R0_CTXNUM_MASK,
+			   EASRC_DPCS0R0_CTXNUM(slot->ctx_index));
+
+	if (ctx->st1_num_taps > 0) {
+		if (ctx->st2_num_taps > 0)
+			st1_mem_alloc =
+				(ctx->st1_num_taps - 1) * slot->num_channel *
+				ctx->st1_num_exp + slot->num_channel;
+		else
+			st1_mem_alloc = ctx->st1_num_taps * slot->num_channel;
+
+		slot->pf_mem_used = st1_mem_alloc;
+		regmap_update_bits(easrc->regmap, reg2,
+				   EASRC_DPCS0R2_ST1_MA_MASK,
+				   EASRC_DPCS0R2_ST1_MA(st1_mem_alloc));
+
+		if (slot_idx == 1)
+			addr = 0x1800 - st1_mem_alloc;
+		else
+			addr = 0;
+
+		regmap_update_bits(easrc->regmap, reg2,
+				   EASRC_DPCS0R2_ST1_SA_MASK,
+				   EASRC_DPCS0R2_ST1_SA(addr));
+	}
+
+	if (ctx->st2_num_taps > 0) {
+		st1_chanxexp = slot->num_channel * (ctx->st1_num_exp - 1);
+
+		regmap_update_bits(easrc->regmap, reg1,
+				   EASRC_DPCS0R1_ST1_EXP_MASK,
+				   EASRC_DPCS0R1_ST1_EXP(st1_chanxexp));
+
+		st2_mem_alloc = slot->num_channel * ctx->st2_num_taps;
+		slot->pf_mem_used += st2_mem_alloc;
+		regmap_update_bits(easrc->regmap, reg3,
+				   EASRC_DPCS0R3_ST2_MA_MASK,
+				   EASRC_DPCS0R3_ST2_MA(st2_mem_alloc));
+
+		if (slot_idx == 1)
+			addr = 0x1800 - st1_mem_alloc - st2_mem_alloc;
+		else
+			addr = st1_mem_alloc;
+
+		regmap_update_bits(easrc->regmap, reg3,
+				   EASRC_DPCS0R3_ST2_SA_MASK,
+				   EASRC_DPCS0R3_ST2_SA(addr));
+	}
+
+	regmap_update_bits(easrc->regmap, reg0,
+			   EASRC_DPCS0R0_EN_MASK, EASRC_DPCS0R0_EN);
+
+	return 0;
+}
+
+/* fsl_easrc_config_slot
+ *
+ * A single context can be split amongst any of the 4 context processing pipes
+ * in the design.
+ * The total number of channels consumed within the context processor must be
+ * less than or equal to 8. if a single context is configured to contain more
+ * than 8 channels then it must be distributed across multiple context
+ * processing pipe slots.
+ *
+ */
+static int fsl_easrc_config_slot(struct fsl_easrc *easrc, unsigned int ctx_id)
+{
+	struct fsl_easrc_context *ctx = easrc->ctx[ctx_id];
+	int req_channels = ctx->channels;
+	int start_channel = 0, avail_channel;
+	struct fsl_easrc_slot *slot0, *slot1;
+	int i, ret;
+
+	if (req_channels <= 0)
+		return -EINVAL;
+
+	for (i = 0; i < EASRC_CTX_MAX_NUM; i++) {
+		slot0 = &easrc->slot[i][0];
+		slot1 = &easrc->slot[i][1];
+
+		if (slot0->busy && slot1->busy)
+			continue;
+
+		if (!slot0->busy) {
+			if (slot1->busy && slot1->ctx_index == ctx->index)
+				continue;
+
+			avail_channel = fsl_easrc_max_ch_for_slot(ctx, slot1);
+			if (avail_channel <= 0)
+				continue;
+
+			ret = fsl_easrc_config_one_slot(ctx,
+							slot0, 0,
+							REG_EASRC_DPCS0R0(i),
+							REG_EASRC_DPCS0R1(i),
+							REG_EASRC_DPCS0R2(i),
+							REG_EASRC_DPCS0R3(i),
+							&req_channels,
+							&start_channel,
+							&avail_channel);
+			if (ret)
+				return ret;
+
+			if (req_channels > 0)
+				continue;
+			else
+				break;
+		}
+
+		if (slot0->busy && !slot1->busy) {
+			if (slot0->ctx_index == ctx->index)
+				continue;
+
+			avail_channel = fsl_easrc_max_ch_for_slot(ctx, slot0);
+			if (avail_channel <= 0)
+				continue;
+
+			ret = fsl_easrc_config_one_slot(ctx,
+							slot1, 1,
+							REG_EASRC_DPCS1R0(i),
+							REG_EASRC_DPCS1R1(i),
+							REG_EASRC_DPCS1R2(i),
+							REG_EASRC_DPCS1R3(i),
+							&req_channels,
+							&start_channel,
+							&avail_channel);
+			if (ret)
+				return ret;
+
+			if (req_channels > 0)
+				continue;
+			else
+				break;
+		}
+	}
+
+	if (req_channels > 0) {
+		dev_err(&easrc->pdev->dev, "no avail slot.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* fsl_easrc_release_slot
+ *
+ * clear the slot configuration
+ */
+static int fsl_easrc_release_slot(struct fsl_easrc *easrc, unsigned int ctx_id)
+{
+	struct fsl_easrc_context *ctx = easrc->ctx[ctx_id];
+	int i;
+
+	for (i = 0; i < EASRC_CTX_MAX_NUM; i++) {
+		if (easrc->slot[i][0].busy &&
+		    easrc->slot[i][0].ctx_index == ctx->index) {
+			easrc->slot[i][0].busy = false;
+			easrc->slot[i][0].num_channel = 0;
+			easrc->slot[i][0].pf_mem_used = 0;
+			/* set registers */
+			regmap_write(easrc->regmap, REG_EASRC_DPCS0R0(i), 0);
+			regmap_write(easrc->regmap, REG_EASRC_DPCS0R1(i), 0);
+			regmap_write(easrc->regmap, REG_EASRC_DPCS0R2(i), 0);
+			regmap_write(easrc->regmap, REG_EASRC_DPCS0R3(i), 0);
+		}
+
+		if (easrc->slot[i][1].busy &&
+		    easrc->slot[i][1].ctx_index == ctx->index) {
+			easrc->slot[i][1].busy = false;
+			easrc->slot[i][1].num_channel = 0;
+			easrc->slot[i][1].pf_mem_used = 0;
+			/* set registers */
+			regmap_write(easrc->regmap, REG_EASRC_DPCS1R0(i), 0);
+			regmap_write(easrc->regmap, REG_EASRC_DPCS1R1(i), 0);
+			regmap_write(easrc->regmap, REG_EASRC_DPCS1R2(i), 0);
+			regmap_write(easrc->regmap, REG_EASRC_DPCS1R3(i), 0);
+		}
+	}
+
+	return 0;
+}
+
+/* fsl_easrc_config_context
+ *
+ * configure the register relate with context.
+ */
+int fsl_easrc_config_context(struct fsl_easrc *easrc, unsigned int ctx_id)
+{
+	struct fsl_easrc_context *ctx;
+	struct device *dev;
+	unsigned long lock_flags;
+	int ret;
+
+	/* to modify prefilter coeficients, the user must perform
+	 * a write in ASRC_PRE_COEFF_FIFO[COEFF_DATA] while the
+	 * RUN_EN for that context is set to 0
+	 */
+	if (!easrc)
+		return -ENODEV;
+
+	dev = &easrc->pdev->dev;
+
+	if (ctx_id >= EASRC_CTX_MAX_NUM) {
+		dev_err(dev, "Invalid context id[%d]\n", ctx_id);
+		return -EINVAL;
+	}
+
+	ctx = easrc->ctx[ctx_id];
+
+	fsl_easrc_normalize_rates(ctx);
+
+	ret = set_rs_ratio(ctx);
+	if (ret)
+		return ret;
+
+	/* initialize the context coeficients */
+	ret = fsl_easrc_prefilter_config(easrc, ctx->index);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&easrc->lock, lock_flags);
+	ret = fsl_easrc_config_slot(easrc, ctx->index);
+	spin_unlock_irqrestore(&easrc->lock, lock_flags);
+	if (ret)
+		return ret;
+
+	/* Both prefilter and resampling filters can use following
+	 * initialization modes:
+	 * 2 - zero-fil mode
+	 * 1 - replication mode
+	 * 0 - software control
+	 */
+	regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+			   EASRC_CCE1_RS_INIT_MASK,
+			   EASRC_CCE1_RS_INIT(ctx->rs_init_mode));
+
+	regmap_update_bits(easrc->regmap, REG_EASRC_CCE1(ctx_id),
+			   EASRC_CCE1_PF_INIT_MASK,
+			   EASRC_CCE1_PF_INIT(ctx->pf_init_mode));
+
+	/* Context Input FIFO Watermark */
+	regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx_id),
+			   EASRC_CC_FIFO_WTMK_MASK,
+			   EASRC_CC_FIFO_WTMK(ctx->in_params.fifo_wtmk));
+
+	/* Context Output FIFO Watermark */
+	regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx_id),
+			   EASRC_COC_FIFO_WTMK_MASK,
+			   EASRC_COC_FIFO_WTMK(ctx->out_params.fifo_wtmk - 1));
+
+	/* number of channels */
+	regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx_id),
+			   EASRC_CC_CHEN_MASK,
+			   EASRC_CC_CHEN(ctx->channels - 1));
+	return 0;
+}
+
+static int fsl_easrc_process_format(struct fsl_easrc_context *ctx,
+				    struct fsl_easrc_data_fmt *fmt,
+				    snd_pcm_format_t raw_fmt)
+{
+	struct fsl_easrc *easrc = ctx->easrc;
+	int ret;
+
+	if (!fmt)
+		return -EINVAL;
+
+	/* Context Input Floating Point Format
+	 * 0 - Integer Format
+	 * 1 - Single Precision FP Format
+	 */
+	fmt->floating_point = !snd_pcm_format_linear(raw_fmt);
+	fmt->sample_pos = 0;
+	fmt->iec958 = 0;
+
+	/* get the data width */
+	switch (snd_pcm_format_width(raw_fmt)) {
+	case 16:
+		fmt->width = EASRC_WIDTH_16_BIT;
+		fmt->addexp = 15;
+		break;
+	case 20:
+		fmt->width = EASRC_WIDTH_20_BIT;
+		fmt->addexp = 19;
+		break;
+	case 24:
+		fmt->width = EASRC_WIDTH_24_BIT;
+		fmt->addexp = 23;
+		break;
+	case 32:
+		fmt->width = EASRC_WIDTH_32_BIT;
+		fmt->addexp = 31;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (raw_fmt) {
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		fmt->width = easrc->bps_iec958[ctx->index];
+		fmt->iec958 = 1;
+		fmt->floating_point = 0;
+		if (fmt->width == EASRC_WIDTH_16_BIT) {
+			fmt->sample_pos = 12;
+			fmt->addexp = 15;
+		} else if (fmt->width == EASRC_WIDTH_20_BIT) {
+			fmt->sample_pos = 8;
+			fmt->addexp = 19;
+		} else if (fmt->width == EASRC_WIDTH_24_BIT) {
+			fmt->sample_pos = 4;
+			fmt->addexp = 23;
+		}
+		break;
+	default:
+		break;
+	}
+
+	/* Data Endianness
+	 * 0 - Little-Endian
+	 * 1 - Big-Endian
+	 */
+	ret = snd_pcm_format_big_endian(raw_fmt);
+	if (ret < 0)
+		return ret;
+
+	fmt->endianness = ret;
+	/* Input Data sign
+	 * 0b - Signed Format
+	 * 1b - Unsigned Format
+	 */
+	fmt->unsign = snd_pcm_format_unsigned(raw_fmt) > 0 ? 1 : 0;
+
+	return 0;
+}
+
+int fsl_easrc_set_ctx_format(struct fsl_easrc_context *ctx,
+			     snd_pcm_format_t *in_raw_format,
+			     snd_pcm_format_t *out_raw_format)
+{
+	struct fsl_easrc *easrc = ctx->easrc;
+	struct fsl_easrc_data_fmt *in_fmt = &ctx->in_params.fmt;
+	struct fsl_easrc_data_fmt *out_fmt = &ctx->out_params.fmt;
+	int ret;
+
+	/* get the bitfield values for input data format */
+	if (in_raw_format && out_raw_format) {
+		ret = fsl_easrc_process_format(ctx, in_fmt, *in_raw_format);
+		if (ret)
+			return ret;
+	}
+
+	regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+			   EASRC_CC_BPS_MASK,
+			   EASRC_CC_BPS(in_fmt->width));
+	regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+			   EASRC_CC_ENDIANNESS_MASK,
+			   in_fmt->endianness << EASRC_CC_ENDIANNESS_SHIFT);
+	regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+			   EASRC_CC_FMT_MASK,
+			   in_fmt->floating_point << EASRC_CC_FMT_SHIFT);
+	regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+			   EASRC_CC_INSIGN_MASK,
+			   in_fmt->unsign << EASRC_CC_INSIGN_SHIFT);
+
+	/* In Sample Position */
+	regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+			   EASRC_CC_SAMPLE_POS_MASK,
+			   EASRC_CC_SAMPLE_POS(in_fmt->sample_pos));
+
+	/* get the bitfield values for input data format */
+	if (in_raw_format && out_raw_format) {
+		ret = fsl_easrc_process_format(ctx, out_fmt, *out_raw_format);
+		if (ret)
+			return ret;
+	}
+
+	regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+			   EASRC_COC_BPS_MASK,
+			   EASRC_COC_BPS(out_fmt->width));
+	regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+			   EASRC_COC_ENDIANNESS_MASK,
+			   out_fmt->endianness << EASRC_COC_ENDIANNESS_SHIFT);
+	regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+			   EASRC_COC_FMT_MASK,
+			   out_fmt->floating_point << EASRC_COC_FMT_SHIFT);
+	regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+			   EASRC_COC_OUTSIGN_MASK,
+			   out_fmt->unsign << EASRC_COC_OUTSIGN_SHIFT);
+
+	/* Out Sample Position */
+	regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+			   EASRC_COC_SAMPLE_POS_MASK,
+			   EASRC_COC_SAMPLE_POS(out_fmt->sample_pos));
+
+	regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+			   EASRC_COC_IEC_EN_MASK,
+			   out_fmt->iec958 << EASRC_COC_IEC_EN_SHIFT);
+
+	return ret;
+}
+
+/* The ASRC provides interleaving support in hardware to ensure that a
+ * variety of sample sources can be internally combined
+ * to conform with this format. Interleaving parameters are accessed
+ * through the ASRC_CTRL_IN_ACCESSa and ASRC_CTRL_OUT_ACCESSa registers
+ */
+int fsl_easrc_set_ctx_organziation(struct fsl_easrc_context *ctx)
+{
+	struct device *dev;
+	struct fsl_easrc *easrc;
+
+	if (!ctx)
+		return -ENODEV;
+
+	easrc = ctx->easrc;
+	dev = &easrc->pdev->dev;
+
+	/* input interleaving parameters */
+	regmap_update_bits(easrc->regmap, REG_EASRC_CIA(ctx->index),
+			   EASRC_CIA_ITER_MASK,
+			   EASRC_CIA_ITER(ctx->in_params.iterations));
+	regmap_update_bits(easrc->regmap, REG_EASRC_CIA(ctx->index),
+			   EASRC_CIA_GRLEN_MASK,
+			   EASRC_CIA_GRLEN(ctx->in_params.group_len));
+	regmap_update_bits(easrc->regmap, REG_EASRC_CIA(ctx->index),
+			   EASRC_CIA_ACCLEN_MASK,
+			   EASRC_CIA_ACCLEN(ctx->in_params.access_len));
+
+	/* output interleaving parameters */
+	regmap_update_bits(easrc->regmap, REG_EASRC_COA(ctx->index),
+			   EASRC_COA_ITER_MASK,
+			   EASRC_COA_ITER(ctx->out_params.iterations));
+	regmap_update_bits(easrc->regmap, REG_EASRC_COA(ctx->index),
+			   EASRC_COA_GRLEN_MASK,
+			   EASRC_COA_GRLEN(ctx->out_params.group_len));
+	regmap_update_bits(easrc->regmap, REG_EASRC_COA(ctx->index),
+			   EASRC_COA_ACCLEN_MASK,
+			   EASRC_COA_ACCLEN(ctx->out_params.access_len));
+
+	return 0;
+}
+
+/* Request one of the available contexts
+ *
+ * Returns a negative number on error and >=0 as context id
+ * on success
+ */
+int fsl_easrc_request_context(struct fsl_easrc_context *ctx,
+			      unsigned int channels)
+{
+	enum asrc_pair_index index = ASRC_INVALID_PAIR;
+	struct fsl_easrc *easrc = ctx->easrc;
+	struct device *dev;
+	unsigned long lock_flags;
+	int ret = 0;
+	int i;
+
+	dev = &easrc->pdev->dev;
+
+	spin_lock_irqsave(&easrc->lock, lock_flags);
+
+	for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) {
+		if (easrc->ctx[i])
+			continue;
+
+		index = i;
+		break;
+	}
+
+	if (index == ASRC_INVALID_PAIR) {
+		dev_err(dev, "all contexts are busy\n");
+		ret = -EBUSY;
+	} else if (channels > easrc->chn_avail) {
+		dev_err(dev, "can't give the required channels: %d\n",
+			channels);
+		ret = -EINVAL;
+	} else {
+		ctx->index = index;
+		ctx->channels = channels;
+		easrc->ctx[index] = ctx;
+		easrc->chn_avail -= channels;
+	}
+
+	spin_unlock_irqrestore(&easrc->lock, lock_flags);
+
+	return ret;
+}
+
+/* Release the context
+ *
+ * This funciton is mainly doing the revert thing in request context
+ */
+int fsl_easrc_release_context(struct fsl_easrc_context *ctx)
+{
+	unsigned long lock_flags;
+	struct fsl_easrc *easrc;
+	struct device *dev;
+	int ret;
+
+	if (!ctx)
+		return 0;
+
+	easrc = ctx->easrc;
+	dev = &easrc->pdev->dev;
+
+	spin_lock_irqsave(&easrc->lock, lock_flags);
+
+	ret = fsl_easrc_release_slot(easrc, ctx->index);
+
+	easrc->chn_avail += ctx->channels;
+	easrc->ctx[ctx->index] = NULL;
+
+	spin_unlock_irqrestore(&easrc->lock, lock_flags);
+
+	return ret;
+}
+
+/* Start the context
+ *
+ * Enable the DMA request and context
+ */
+int fsl_easrc_start_context(struct fsl_easrc_context *ctx)
+{
+	struct fsl_easrc *easrc = ctx->easrc;
+
+	regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+			   EASRC_CC_FWMDE_MASK,
+			   EASRC_CC_FWMDE);
+	regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+			   EASRC_COC_FWMDE_MASK,
+			   EASRC_COC_FWMDE);
+	regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+			   EASRC_CC_EN_MASK,
+			   EASRC_CC_EN);
+	return 0;
+}
+
+/* Stop the context
+ *
+ * Disable the DMA request and context
+ */
+int fsl_easrc_stop_context(struct fsl_easrc_context *ctx)
+{
+	struct fsl_easrc *easrc = ctx->easrc;
+	int val, i;
+	int size = 0;
+	int retry = 200;
+
+	regmap_read(easrc->regmap, REG_EASRC_CC(ctx->index), &val);
+
+	if (val & EASRC_CC_EN_MASK) {
+		regmap_update_bits(easrc->regmap,
+				   REG_EASRC_CC(ctx->index),
+				   EASRC_CC_STOP_MASK, EASRC_CC_STOP);
+		do {
+			regmap_read(easrc->regmap, REG_EASRC_SFS(ctx->index), &val);
+			val &= EASRC_SFS_NSGO_MASK;
+			size = val >> EASRC_SFS_NSGO_SHIFT;
+
+			/* Read FIFO, drop the data */
+			for (i = 0; i < size * ctx->channels; i++)
+				regmap_read(easrc->regmap, REG_EASRC_RDFIFO(ctx->index), &val);
+			/* Check RUN_STOP_DONE */
+			regmap_read(easrc->regmap, REG_EASRC_IRQF, &val);
+			if (val & EASRC_IRQF_RSD(1 << ctx->index)) {
+				/*Clear RUN_STOP_DONE*/
+				regmap_write_bits(easrc->regmap,
+						  REG_EASRC_IRQF,
+						  EASRC_IRQF_RSD(1 << ctx->index),
+						  EASRC_IRQF_RSD(1 << ctx->index));
+				break;
+			}
+			udelay(100);
+		} while (--retry);
+
+		if (retry == 0)
+			dev_warn(&easrc->pdev->dev, "RUN STOP fail\n");
+	}
+
+	regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+			   EASRC_CC_EN_MASK | EASRC_CC_STOP_MASK, 0);
+	regmap_update_bits(easrc->regmap, REG_EASRC_CC(ctx->index),
+			   EASRC_CC_FWMDE_MASK, 0);
+	regmap_update_bits(easrc->regmap, REG_EASRC_COC(ctx->index),
+			   EASRC_COC_FWMDE_MASK, 0);
+	return 0;
+}
+
+struct dma_chan *fsl_easrc_get_dma_channel(struct fsl_easrc_context *ctx,
+					   bool dir)
+{
+	struct fsl_easrc *easrc = ctx->easrc;
+	enum asrc_pair_index index = ctx->index;
+	char name[8];
+
+	/* example of dma name: ctx0_rx */
+	sprintf(name, "ctx%c_%cx", index + '0', dir == IN ? 'r' : 't');
+
+	return dma_request_slave_channel(&easrc->pdev->dev, name);
+};
+EXPORT_SYMBOL_GPL(fsl_easrc_get_dma_channel);
+
+static const unsigned int easrc_rates[] = {
+	8000, 11025, 12000, 16000,
+	22050, 24000, 32000, 44100,
+	48000, 64000, 88200, 96000,
+	128000, 176400, 192000, 256000,
+	352800, 384000, 705600, 768000,
+};
+
+static const struct snd_pcm_hw_constraint_list easrc_rate_constraints = {
+	.count = ARRAY_SIZE(easrc_rates),
+	.list = easrc_rates,
+};
+
+static int fsl_easrc_startup(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+					  SNDRV_PCM_HW_PARAM_RATE,
+					  &easrc_rate_constraints);
+}
+
+static int fsl_easrc_trigger(struct snd_pcm_substream *substream,
+			     int cmd, struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_easrc_context *ctx = runtime->private_data;
+	int ret;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ret = fsl_easrc_start_context(ctx);
+		if (ret)
+			return ret;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ret = fsl_easrc_stop_context(ctx);
+		if (ret)
+			return ret;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int fsl_easrc_hw_params(struct snd_pcm_substream *substream,
+			       struct snd_pcm_hw_params *params,
+			       struct snd_soc_dai *dai)
+{
+	struct fsl_easrc *easrc = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct device *dev = &easrc->pdev->dev;
+	struct fsl_easrc_context *ctx = runtime->private_data;
+	unsigned int channels = params_channels(params);
+	unsigned int rate = params_rate(params);
+	snd_pcm_format_t format = params_format(params);
+	int ret;
+
+	ret = fsl_easrc_request_context(ctx, channels);
+	if (ret) {
+		dev_err(dev, "failed to request context\n");
+		return ret;
+	}
+
+	ctx->ctx_streams |= BIT(substream->stream);
+
+	/* set the input and output ratio so we can compute
+	 * the resampling ratio in RS_LOW/HIGH
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ctx->in_params.sample_rate = rate;
+		ctx->in_params.sample_format = format;
+		ctx->out_params.sample_rate = easrc->easrc_rate;
+		ctx->out_params.sample_format = easrc->easrc_format;
+	} else {
+		ctx->out_params.sample_rate = rate;
+		ctx->out_params.sample_format = format;
+		ctx->in_params.sample_rate = easrc->easrc_rate;
+		ctx->in_params.sample_format = easrc->easrc_format;
+	}
+
+	ctx->channels = channels;
+	ctx->in_params.fifo_wtmk  = 0x20;
+	ctx->out_params.fifo_wtmk = 0x20;
+
+	/* do only rate conversion and keep the same format for input
+	 * and output data
+	 */
+	ret = fsl_easrc_set_ctx_format(ctx,
+				       &ctx->in_params.sample_format,
+				       &ctx->out_params.sample_format);
+	if (ret) {
+		dev_err(dev, "failed to set format %d", ret);
+		return ret;
+	}
+
+	ret = fsl_easrc_config_context(easrc, ctx->index);
+	if (ret) {
+		dev_err(dev, "failed to config context\n");
+		return ret;
+	}
+
+	ctx->in_params.iterations = 1;
+	ctx->in_params.group_len = ctx->channels;
+	ctx->in_params.access_len = ctx->channels;
+	ctx->out_params.iterations = 1;
+	ctx->out_params.group_len = ctx->channels;
+	ctx->out_params.access_len = ctx->channels;
+
+	ret = fsl_easrc_set_ctx_organziation(ctx);
+	if (ret) {
+		dev_err(dev, "failed to set fifo organization\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fsl_easrc_hw_free(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_easrc_context *ctx = runtime->private_data;
+	int ret;
+
+	if (ctx && (ctx->ctx_streams & BIT(substream->stream))) {
+		ctx->ctx_streams &= ~BIT(substream->stream);
+		ret = fsl_easrc_release_context(ctx);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops fsl_easrc_dai_ops = {
+	.startup = fsl_easrc_startup,
+	.trigger = fsl_easrc_trigger,
+	.hw_params = fsl_easrc_hw_params,
+	.hw_free = fsl_easrc_hw_free,
+};
+
+static int fsl_easrc_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+	struct fsl_easrc *easrc = dev_get_drvdata(cpu_dai->dev);
+
+	snd_soc_dai_init_dma_data(cpu_dai,
+				  &easrc->dma_params_tx,
+				  &easrc->dma_params_rx);
+	return 0;
+}
+
+static struct snd_soc_dai_driver fsl_easrc_dai = {
+	.probe = fsl_easrc_dai_probe,
+	.playback = {
+		.stream_name = "ASRC-Playback",
+		.channels_min = 1,
+		.channels_max = 32,
+		.rate_min = 8000,
+		.rate_max = 768000,
+		.rates = SNDRV_PCM_RATE_KNOT,
+		.formats = FSL_EASRC_FORMATS,
+	},
+	.capture = {
+		.stream_name = "ASRC-Capture",
+		.channels_min = 1,
+		.channels_max = 32,
+		.rate_min = 8000,
+		.rate_max = 768000,
+		.rates = SNDRV_PCM_RATE_KNOT,
+		.formats = FSL_EASRC_FORMATS |
+			   SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+	},
+	.ops = &fsl_easrc_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_easrc_component = {
+	.name		= "fsl-easrc-dai",
+	.controls       = fsl_easrc_snd_controls,
+	.num_controls   = ARRAY_SIZE(fsl_easrc_snd_controls),
+};
+
+static const struct reg_default fsl_easrc_reg_defaults[] = {
+	{REG_EASRC_WRFIFO(0),	0x00000000},
+	{REG_EASRC_WRFIFO(1),	0x00000000},
+	{REG_EASRC_WRFIFO(2),	0x00000000},
+	{REG_EASRC_WRFIFO(3),	0x00000000},
+	{REG_EASRC_RDFIFO(0),	0x00000000},
+	{REG_EASRC_RDFIFO(1),	0x00000000},
+	{REG_EASRC_RDFIFO(2),	0x00000000},
+	{REG_EASRC_RDFIFO(3),	0x00000000},
+	{REG_EASRC_CC(0),	0x00000000},
+	{REG_EASRC_CC(1),	0x00000000},
+	{REG_EASRC_CC(2),	0x00000000},
+	{REG_EASRC_CC(3),	0x00000000},
+	{REG_EASRC_CCE1(0),	0x00000000},
+	{REG_EASRC_CCE1(1),	0x00000000},
+	{REG_EASRC_CCE1(2),	0x00000000},
+	{REG_EASRC_CCE1(3),	0x00000000},
+	{REG_EASRC_CCE2(0),	0x00000000},
+	{REG_EASRC_CCE2(1),	0x00000000},
+	{REG_EASRC_CCE2(2),	0x00000000},
+	{REG_EASRC_CCE2(3),	0x00000000},
+	{REG_EASRC_CIA(0),	0x00000000},
+	{REG_EASRC_CIA(1),	0x00000000},
+	{REG_EASRC_CIA(2),	0x00000000},
+	{REG_EASRC_CIA(3),	0x00000000},
+	{REG_EASRC_DPCS0R0(0),	0x00000000},
+	{REG_EASRC_DPCS0R0(1),	0x00000000},
+	{REG_EASRC_DPCS0R0(2),	0x00000000},
+	{REG_EASRC_DPCS0R0(3),	0x00000000},
+	{REG_EASRC_DPCS0R1(0),	0x00000000},
+	{REG_EASRC_DPCS0R1(1),	0x00000000},
+	{REG_EASRC_DPCS0R1(2),	0x00000000},
+	{REG_EASRC_DPCS0R1(3),	0x00000000},
+	{REG_EASRC_DPCS0R2(0),	0x00000000},
+	{REG_EASRC_DPCS0R2(1),	0x00000000},
+	{REG_EASRC_DPCS0R2(2),	0x00000000},
+	{REG_EASRC_DPCS0R2(3),	0x00000000},
+	{REG_EASRC_DPCS0R3(0),	0x00000000},
+	{REG_EASRC_DPCS0R3(1),	0x00000000},
+	{REG_EASRC_DPCS0R3(2),	0x00000000},
+	{REG_EASRC_DPCS0R3(3),	0x00000000},
+	{REG_EASRC_DPCS1R0(0),	0x00000000},
+	{REG_EASRC_DPCS1R0(1),	0x00000000},
+	{REG_EASRC_DPCS1R0(2),	0x00000000},
+	{REG_EASRC_DPCS1R0(3),	0x00000000},
+	{REG_EASRC_DPCS1R1(0),	0x00000000},
+	{REG_EASRC_DPCS1R1(1),	0x00000000},
+	{REG_EASRC_DPCS1R1(2),	0x00000000},
+	{REG_EASRC_DPCS1R1(3),	0x00000000},
+	{REG_EASRC_DPCS1R2(0),	0x00000000},
+	{REG_EASRC_DPCS1R2(1),	0x00000000},
+	{REG_EASRC_DPCS1R2(2),	0x00000000},
+	{REG_EASRC_DPCS1R2(3),	0x00000000},
+	{REG_EASRC_DPCS1R3(0),	0x00000000},
+	{REG_EASRC_DPCS1R3(1),	0x00000000},
+	{REG_EASRC_DPCS1R3(2),	0x00000000},
+	{REG_EASRC_DPCS1R3(3),	0x00000000},
+	{REG_EASRC_COC(0),	0x00000000},
+	{REG_EASRC_COC(1),	0x00000000},
+	{REG_EASRC_COC(2),	0x00000000},
+	{REG_EASRC_COC(3),	0x00000000},
+	{REG_EASRC_COA(0),	0x00000000},
+	{REG_EASRC_COA(1),	0x00000000},
+	{REG_EASRC_COA(2),	0x00000000},
+	{REG_EASRC_COA(3),	0x00000000},
+	{REG_EASRC_SFS(0),	0x00000000},
+	{REG_EASRC_SFS(1),	0x00000000},
+	{REG_EASRC_SFS(2),	0x00000000},
+	{REG_EASRC_SFS(3),	0x00000000},
+	{REG_EASRC_RRL(0),	0x00000000},
+	{REG_EASRC_RRL(1),	0x00000000},
+	{REG_EASRC_RRL(2),	0x00000000},
+	{REG_EASRC_RRL(3),	0x00000000},
+	{REG_EASRC_RRH(0),	0x00000000},
+	{REG_EASRC_RRH(1),	0x00000000},
+	{REG_EASRC_RRH(2),	0x00000000},
+	{REG_EASRC_RRH(3),	0x00000000},
+	{REG_EASRC_RUC(0),	0x00000000},
+	{REG_EASRC_RUC(1),	0x00000000},
+	{REG_EASRC_RUC(2),	0x00000000},
+	{REG_EASRC_RUC(3),	0x00000000},
+	{REG_EASRC_RUR(0),	0x7FFFFFFF},
+	{REG_EASRC_RUR(1),	0x7FFFFFFF},
+	{REG_EASRC_RUR(2),	0x7FFFFFFF},
+	{REG_EASRC_RUR(3),	0x7FFFFFFF},
+	{REG_EASRC_RCTCL,	0x00000000},
+	{REG_EASRC_RCTCH,	0x00000000},
+	{REG_EASRC_PCF(0),	0x00000000},
+	{REG_EASRC_PCF(1),	0x00000000},
+	{REG_EASRC_PCF(2),	0x00000000},
+	{REG_EASRC_PCF(3),	0x00000000},
+	{REG_EASRC_CRCM,	0x00000000},
+	{REG_EASRC_CRCC,	0x00000000},
+	{REG_EASRC_IRQC,	0x00000FFF},
+	{REG_EASRC_IRQF,	0x00000000},
+	{REG_EASRC_CS0(0),	0x00000000},
+	{REG_EASRC_CS0(1),	0x00000000},
+	{REG_EASRC_CS0(2),	0x00000000},
+	{REG_EASRC_CS0(3),	0x00000000},
+	{REG_EASRC_CS1(0),	0x00000000},
+	{REG_EASRC_CS1(1),	0x00000000},
+	{REG_EASRC_CS1(2),	0x00000000},
+	{REG_EASRC_CS1(3),	0x00000000},
+	{REG_EASRC_CS2(0),	0x00000000},
+	{REG_EASRC_CS2(1),	0x00000000},
+	{REG_EASRC_CS2(2),	0x00000000},
+	{REG_EASRC_CS2(3),	0x00000000},
+	{REG_EASRC_CS3(0),	0x00000000},
+	{REG_EASRC_CS3(1),	0x00000000},
+	{REG_EASRC_CS3(2),	0x00000000},
+	{REG_EASRC_CS3(3),	0x00000000},
+	{REG_EASRC_CS4(0),	0x00000000},
+	{REG_EASRC_CS4(1),	0x00000000},
+	{REG_EASRC_CS4(2),	0x00000000},
+	{REG_EASRC_CS4(3),	0x00000000},
+	{REG_EASRC_CS5(0),	0x00000000},
+	{REG_EASRC_CS5(1),	0x00000000},
+	{REG_EASRC_CS5(2),	0x00000000},
+	{REG_EASRC_CS5(3),	0x00000000},
+	{REG_EASRC_DBGC,	0x00000000},
+	{REG_EASRC_DBGS,	0x00000000},
+};
+
+static bool fsl_easrc_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case REG_EASRC_RDFIFO(0):
+	case REG_EASRC_RDFIFO(1):
+	case REG_EASRC_RDFIFO(2):
+	case REG_EASRC_RDFIFO(3):
+	case REG_EASRC_CC(0):
+	case REG_EASRC_CC(1):
+	case REG_EASRC_CC(2):
+	case REG_EASRC_CC(3):
+	case REG_EASRC_CCE1(0):
+	case REG_EASRC_CCE1(1):
+	case REG_EASRC_CCE1(2):
+	case REG_EASRC_CCE1(3):
+	case REG_EASRC_CCE2(0):
+	case REG_EASRC_CCE2(1):
+	case REG_EASRC_CCE2(2):
+	case REG_EASRC_CCE2(3):
+	case REG_EASRC_CIA(0):
+	case REG_EASRC_CIA(1):
+	case REG_EASRC_CIA(2):
+	case REG_EASRC_CIA(3):
+	case REG_EASRC_DPCS0R0(0):
+	case REG_EASRC_DPCS0R0(1):
+	case REG_EASRC_DPCS0R0(2):
+	case REG_EASRC_DPCS0R0(3):
+	case REG_EASRC_DPCS0R1(0):
+	case REG_EASRC_DPCS0R1(1):
+	case REG_EASRC_DPCS0R1(2):
+	case REG_EASRC_DPCS0R1(3):
+	case REG_EASRC_DPCS0R2(0):
+	case REG_EASRC_DPCS0R2(1):
+	case REG_EASRC_DPCS0R2(2):
+	case REG_EASRC_DPCS0R2(3):
+	case REG_EASRC_DPCS0R3(0):
+	case REG_EASRC_DPCS0R3(1):
+	case REG_EASRC_DPCS0R3(2):
+	case REG_EASRC_DPCS0R3(3):
+	case REG_EASRC_DPCS1R0(0):
+	case REG_EASRC_DPCS1R0(1):
+	case REG_EASRC_DPCS1R0(2):
+	case REG_EASRC_DPCS1R0(3):
+	case REG_EASRC_DPCS1R1(0):
+	case REG_EASRC_DPCS1R1(1):
+	case REG_EASRC_DPCS1R1(2):
+	case REG_EASRC_DPCS1R1(3):
+	case REG_EASRC_DPCS1R2(0):
+	case REG_EASRC_DPCS1R2(1):
+	case REG_EASRC_DPCS1R2(2):
+	case REG_EASRC_DPCS1R2(3):
+	case REG_EASRC_DPCS1R3(0):
+	case REG_EASRC_DPCS1R3(1):
+	case REG_EASRC_DPCS1R3(2):
+	case REG_EASRC_DPCS1R3(3):
+	case REG_EASRC_COC(0):
+	case REG_EASRC_COC(1):
+	case REG_EASRC_COC(2):
+	case REG_EASRC_COC(3):
+	case REG_EASRC_COA(0):
+	case REG_EASRC_COA(1):
+	case REG_EASRC_COA(2):
+	case REG_EASRC_COA(3):
+	case REG_EASRC_SFS(0):
+	case REG_EASRC_SFS(1):
+	case REG_EASRC_SFS(2):
+	case REG_EASRC_SFS(3):
+	case REG_EASRC_RRL(0):
+	case REG_EASRC_RRL(1):
+	case REG_EASRC_RRL(2):
+	case REG_EASRC_RRL(3):
+	case REG_EASRC_RRH(0):
+	case REG_EASRC_RRH(1):
+	case REG_EASRC_RRH(2):
+	case REG_EASRC_RRH(3):
+	case REG_EASRC_RUC(0):
+	case REG_EASRC_RUC(1):
+	case REG_EASRC_RUC(2):
+	case REG_EASRC_RUC(3):
+	case REG_EASRC_RUR(0):
+	case REG_EASRC_RUR(1):
+	case REG_EASRC_RUR(2):
+	case REG_EASRC_RUR(3): /* fallthrough */
+	case REG_EASRC_RCTCL:
+	case REG_EASRC_RCTCH:
+	case REG_EASRC_PCF(0):
+	case REG_EASRC_PCF(1):
+	case REG_EASRC_PCF(2):
+	case REG_EASRC_PCF(3): /* fallthrough */
+	case REG_EASRC_CRCC:
+	case REG_EASRC_IRQC:
+	case REG_EASRC_IRQF:
+	case REG_EASRC_CS0(0):
+	case REG_EASRC_CS0(1):
+	case REG_EASRC_CS0(2):
+	case REG_EASRC_CS0(3):
+	case REG_EASRC_CS1(0):
+	case REG_EASRC_CS1(1):
+	case REG_EASRC_CS1(2):
+	case REG_EASRC_CS1(3):
+	case REG_EASRC_CS2(0):
+	case REG_EASRC_CS2(1):
+	case REG_EASRC_CS2(2):
+	case REG_EASRC_CS2(3):
+	case REG_EASRC_CS3(0):
+	case REG_EASRC_CS3(1):
+	case REG_EASRC_CS3(2):
+	case REG_EASRC_CS3(3):
+	case REG_EASRC_CS4(0):
+	case REG_EASRC_CS4(1):
+	case REG_EASRC_CS4(2):
+	case REG_EASRC_CS4(3):
+	case REG_EASRC_CS5(0):
+	case REG_EASRC_CS5(1):
+	case REG_EASRC_CS5(2):
+	case REG_EASRC_CS5(3): /* fallthrough */
+	case REG_EASRC_DBGC:
+	case REG_EASRC_DBGS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool fsl_easrc_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case REG_EASRC_WRFIFO(0):
+	case REG_EASRC_WRFIFO(1):
+	case REG_EASRC_WRFIFO(2):
+	case REG_EASRC_WRFIFO(3):
+	case REG_EASRC_CC(0):
+	case REG_EASRC_CC(1):
+	case REG_EASRC_CC(2):
+	case REG_EASRC_CC(3):
+	case REG_EASRC_CCE1(0):
+	case REG_EASRC_CCE1(1):
+	case REG_EASRC_CCE1(2):
+	case REG_EASRC_CCE1(3):
+	case REG_EASRC_CCE2(0):
+	case REG_EASRC_CCE2(1):
+	case REG_EASRC_CCE2(2):
+	case REG_EASRC_CCE2(3):
+	case REG_EASRC_CIA(0):
+	case REG_EASRC_CIA(1):
+	case REG_EASRC_CIA(2):
+	case REG_EASRC_CIA(3):
+	case REG_EASRC_DPCS0R0(0):
+	case REG_EASRC_DPCS0R0(1):
+	case REG_EASRC_DPCS0R0(2):
+	case REG_EASRC_DPCS0R0(3):
+	case REG_EASRC_DPCS0R1(0):
+	case REG_EASRC_DPCS0R1(1):
+	case REG_EASRC_DPCS0R1(2):
+	case REG_EASRC_DPCS0R1(3):
+	case REG_EASRC_DPCS0R2(0):
+	case REG_EASRC_DPCS0R2(1):
+	case REG_EASRC_DPCS0R2(2):
+	case REG_EASRC_DPCS0R2(3):
+	case REG_EASRC_DPCS0R3(0):
+	case REG_EASRC_DPCS0R3(1):
+	case REG_EASRC_DPCS0R3(2):
+	case REG_EASRC_DPCS0R3(3):
+	case REG_EASRC_DPCS1R0(0):
+	case REG_EASRC_DPCS1R0(1):
+	case REG_EASRC_DPCS1R0(2):
+	case REG_EASRC_DPCS1R0(3):
+	case REG_EASRC_DPCS1R1(0):
+	case REG_EASRC_DPCS1R1(1):
+	case REG_EASRC_DPCS1R1(2):
+	case REG_EASRC_DPCS1R1(3):
+	case REG_EASRC_DPCS1R2(0):
+	case REG_EASRC_DPCS1R2(1):
+	case REG_EASRC_DPCS1R2(2):
+	case REG_EASRC_DPCS1R2(3):
+	case REG_EASRC_DPCS1R3(0):
+	case REG_EASRC_DPCS1R3(1):
+	case REG_EASRC_DPCS1R3(2):
+	case REG_EASRC_DPCS1R3(3):
+	case REG_EASRC_COC(0):
+	case REG_EASRC_COC(1):
+	case REG_EASRC_COC(2):
+	case REG_EASRC_COC(3):
+	case REG_EASRC_COA(0):
+	case REG_EASRC_COA(1):
+	case REG_EASRC_COA(2):
+	case REG_EASRC_COA(3):
+	case REG_EASRC_RRL(0):
+	case REG_EASRC_RRL(1):
+	case REG_EASRC_RRL(2):
+	case REG_EASRC_RRL(3):
+	case REG_EASRC_RRH(0):
+	case REG_EASRC_RRH(1):
+	case REG_EASRC_RRH(2):
+	case REG_EASRC_RRH(3):
+	case REG_EASRC_RUC(0):
+	case REG_EASRC_RUC(1):
+	case REG_EASRC_RUC(2):
+	case REG_EASRC_RUC(3):
+	case REG_EASRC_RUR(0):
+	case REG_EASRC_RUR(1):
+	case REG_EASRC_RUR(2):
+	case REG_EASRC_RUR(3):  /* fallthrough */
+	case REG_EASRC_RCTCL:
+	case REG_EASRC_RCTCH:
+	case REG_EASRC_PCF(0):
+	case REG_EASRC_PCF(1):
+	case REG_EASRC_PCF(2):
+	case REG_EASRC_PCF(3):  /* fallthrough */
+	case REG_EASRC_CRCM:
+	case REG_EASRC_CRCC:
+	case REG_EASRC_IRQC:
+	case REG_EASRC_IRQF:
+	case REG_EASRC_CS0(0):
+	case REG_EASRC_CS0(1):
+	case REG_EASRC_CS0(2):
+	case REG_EASRC_CS0(3):
+	case REG_EASRC_CS1(0):
+	case REG_EASRC_CS1(1):
+	case REG_EASRC_CS1(2):
+	case REG_EASRC_CS1(3):
+	case REG_EASRC_CS2(0):
+	case REG_EASRC_CS2(1):
+	case REG_EASRC_CS2(2):
+	case REG_EASRC_CS2(3):
+	case REG_EASRC_CS3(0):
+	case REG_EASRC_CS3(1):
+	case REG_EASRC_CS3(2):
+	case REG_EASRC_CS3(3):
+	case REG_EASRC_CS4(0):
+	case REG_EASRC_CS4(1):
+	case REG_EASRC_CS4(2):
+	case REG_EASRC_CS4(3):
+	case REG_EASRC_CS5(0):
+	case REG_EASRC_CS5(1):
+	case REG_EASRC_CS5(2):
+	case REG_EASRC_CS5(3): /* fallthrough */
+	case REG_EASRC_DBGC:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool fsl_easrc_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case REG_EASRC_RDFIFO(0):
+	case REG_EASRC_RDFIFO(1):
+	case REG_EASRC_RDFIFO(2):
+	case REG_EASRC_RDFIFO(3):
+	case REG_EASRC_SFS(0):
+	case REG_EASRC_SFS(1):
+	case REG_EASRC_SFS(2):
+	case REG_EASRC_SFS(3): /* fallthrough */
+	case REG_EASRC_IRQF:
+	case REG_EASRC_DBGS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config fsl_easrc_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+
+	.max_register = REG_EASRC_DBGS,
+	.reg_defaults = fsl_easrc_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(fsl_easrc_reg_defaults),
+	.readable_reg = fsl_easrc_readable_reg,
+	.volatile_reg = fsl_easrc_volatile_reg,
+	.writeable_reg = fsl_easrc_writeable_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+void easrc_dump_firmware(struct fsl_easrc *easrc)
+{
+	struct device *dev = &easrc->pdev->dev;
+	struct asrc_firmware_hdr *firm = easrc->firmware_hdr;
+	struct interp_params *interp = easrc->interp;
+	struct prefil_params *prefil = easrc->prefil;
+	int i;
+
+	if (firm->magic != FIRMWARE_MAGIC) {
+		dev_err(dev, "Wrong magic. Something went wrong!");
+		return;
+	}
+
+	dev_dbg(dev, "Firmware v%u dump:\n", firm->firmware_version);
+	pr_debug("Num prefitler scenarios: %u\n", firm->prefil_scen);
+	pr_debug("Num interpolation scenarios: %u\n", firm->interp_scen);
+	pr_debug("\nInterpolation scenarios:\n");
+
+	for (i = 0; i < firm->interp_scen; i++) {
+		if (interp[i].magic != FIRMWARE_MAGIC) {
+			pr_debug("%d. wrong interp magic: %x\n",
+				 i, interp[i].magic);
+			continue;
+		}
+		pr_debug("%d. taps: %u, phases: %u, center: %llu\n", i,
+			 interp[i].num_taps, interp[i].num_phases,
+			 interp[i].center_tap);
+	}
+
+	for (i = 0; i < firm->prefil_scen; i++) {
+		if (prefil[i].magic != FIRMWARE_MAGIC) {
+			pr_debug("%d. wrong prefil magic: %x\n",
+				 i, prefil[i].magic);
+			continue;
+		}
+		pr_debug("%d. insr: %u, outsr: %u, st1: %u, st2: %u\n", i,
+			 prefil[i].insr, prefil[i].outsr,
+			 prefil[i].st1_taps, prefil[i].st2_taps);
+	}
+
+	dev_dbg(dev, "end of firmware dump\n");
+}
+
+int easrc_get_firmware(struct fsl_easrc *easrc)
+{
+	u32 pnum, inum, offset;
+	int ret;
+
+	if (!easrc)
+		return -EINVAL;
+
+	ret = request_firmware(&easrc->fw, easrc->fw_name,
+			       &easrc->pdev->dev);
+	if (ret)
+		return ret;
+
+	easrc->firmware_hdr = (struct asrc_firmware_hdr *)easrc->fw->data;
+	pnum = easrc->firmware_hdr->prefil_scen;
+	inum = easrc->firmware_hdr->interp_scen;
+
+	if (inum) {
+		offset = sizeof(struct asrc_firmware_hdr);
+		easrc->interp =
+			(struct interp_params *)(easrc->fw->data + offset);
+	}
+
+	if (pnum) {
+		offset = sizeof(struct asrc_firmware_hdr) +
+				inum * sizeof(struct interp_params);
+		easrc->prefil =
+			(struct prefil_params *)(easrc->fw->data + offset);
+	}
+
+	return 0;
+}
+
+static irqreturn_t fsl_easrc_isr(int irq, void *dev_id)
+{
+	struct fsl_easrc *easrc = (struct fsl_easrc *)dev_id;
+	struct device *dev = &easrc->pdev->dev;
+	int val;
+
+	regmap_read(easrc->regmap, REG_EASRC_IRQF, &val);
+
+	if (val & EASRC_IRQF_OER_MASK)
+		dev_dbg(dev, "output FIFO underflow\n");
+
+	if (val & EASRC_IRQF_IFO_MASK)
+		dev_dbg(dev, "input FIFO overflow\n");
+
+	return IRQ_HANDLED;
+}
+
+static const struct of_device_id fsl_easrc_dt_ids[] = {
+	{ .compatible = "fsl,imx8mn-easrc",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, fsl_easrc_dt_ids);
+
+static int fsl_easrc_probe(struct platform_device *pdev)
+{
+	struct fsl_easrc *easrc;
+	struct resource *res;
+	struct device_node *np;
+	void __iomem *regs;
+	int ret, irq;
+	u32 width;
+
+	easrc = devm_kzalloc(&pdev->dev, sizeof(*easrc), GFP_KERNEL);
+	if (!easrc)
+		return -ENOMEM;
+
+	easrc->pdev = pdev;
+	np = pdev->dev.of_node;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(regs)) {
+		dev_err(&pdev->dev, "failed ioremap\n");
+		return PTR_ERR(regs);
+	}
+
+	easrc->paddr = res->start;
+
+	easrc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs,
+						  &fsl_easrc_regmap_config);
+	if (IS_ERR(easrc->regmap)) {
+		dev_err(&pdev->dev, "failed to init regmap");
+		return PTR_ERR(easrc->regmap);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq for node %s\n",
+			dev_name(&pdev->dev));
+		return irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, fsl_easrc_isr, 0,
+			       dev_name(&pdev->dev), easrc);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
+		return ret;
+	}
+
+	easrc->mem_clk = devm_clk_get(&pdev->dev, "mem");
+	if (IS_ERR(easrc->mem_clk)) {
+		dev_err(&pdev->dev, "failed to get mem clock\n");
+		return PTR_ERR(easrc->mem_clk);
+	}
+
+	/*Set default value*/
+	easrc->chn_avail = 32;
+	easrc->rs_num_taps = EASRC_RS_128_TAPS;
+	easrc->const_coeff = 0x3FF0000000000000;
+
+	ret = of_property_read_u32(np, "fsl,asrc-rate",
+				   &easrc->easrc_rate);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to asrc rate\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "fsl,asrc-width",
+				   &width);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to asrc width\n");
+		return ret;
+	}
+
+	if (width != 16 && width != 24 && width != 32 && width != 20) {
+		dev_warn(&pdev->dev, "unsupported width, switching to 24bit\n");
+		width = 24;
+	}
+
+	if (width == 24)
+		easrc->easrc_format = SNDRV_PCM_FORMAT_S24_LE;
+	else if (width == 16)
+		easrc->easrc_format = SNDRV_PCM_FORMAT_S16_LE;
+	else
+		easrc->easrc_format = SNDRV_PCM_FORMAT_S32_LE;
+
+	platform_set_drvdata(pdev, easrc);
+	pm_runtime_enable(&pdev->dev);
+
+	spin_lock_init(&easrc->lock);
+
+	regcache_cache_only(easrc->regmap, true);
+
+	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_easrc_component,
+					      &fsl_easrc_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register ASoC DAI\n");
+		return ret;
+	}
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      &fsl_easrc_dma_component,
+					      NULL, 0);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register ASoC platform\n");
+		return ret;
+	}
+
+	ret = of_property_read_string(np,
+				      "fsl,easrc-ram-script-name",
+				      &easrc->fw_name);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to get firmware name\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fsl_easrc_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int fsl_easrc_runtime_suspend(struct device *dev)
+{
+	struct fsl_easrc *easrc = dev_get_drvdata(dev);
+	unsigned long lock_flags;
+
+	regcache_cache_only(easrc->regmap, true);
+
+	clk_disable_unprepare(easrc->mem_clk);
+
+	spin_lock_irqsave(&easrc->lock, lock_flags);
+	easrc->firmware_loaded = 0;
+	spin_unlock_irqrestore(&easrc->lock, lock_flags);
+
+	return 0;
+}
+
+static int fsl_easrc_runtime_resume(struct device *dev)
+{
+	struct fsl_easrc *easrc = dev_get_drvdata(dev);
+	struct fsl_easrc_context *ctx;
+	unsigned long lock_flags;
+	int ret;
+	int i;
+
+	ret = clk_prepare_enable(easrc->mem_clk);
+	if (ret)
+		return ret;
+
+	regcache_cache_only(easrc->regmap, false);
+	regcache_mark_dirty(easrc->regmap);
+	regcache_sync(easrc->regmap);
+
+	spin_lock_irqsave(&easrc->lock, lock_flags);
+	if (easrc->firmware_loaded) {
+		spin_unlock_irqrestore(&easrc->lock, lock_flags);
+		goto skip_load;
+	}
+	easrc->firmware_loaded = 1;
+	spin_unlock_irqrestore(&easrc->lock, lock_flags);
+
+	ret = easrc_get_firmware(easrc);
+	if (ret) {
+		dev_err(dev, "failed to get firmware\n");
+		goto disable_mem_clk;
+	}
+
+	/* Write Resampling Coefficients
+	 * The coefficient RAM must be configured prior to beginning of
+	 * any context processing within the ASRC
+	 */
+	ret = fsl_easrc_resampler_config(easrc);
+	if (ret) {
+		dev_err(dev, "resampler config failed\n");
+		goto disable_mem_clk;
+	}
+
+	for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) {
+		ctx = easrc->ctx[i];
+		if (ctx) {
+			set_rs_ratio(ctx);
+			ctx->out_missed_sample = ctx->in_filled_sample *
+						ctx->out_params.sample_rate /
+						ctx->in_params.sample_rate;
+			if (ctx->in_filled_sample * ctx->out_params.sample_rate
+					% ctx->in_params.sample_rate != 0)
+				ctx->out_missed_sample += 1;
+
+			ret = write_pf_coeff_mem(easrc, i,
+						 ctx->st1_coeff,
+						 ctx->st1_num_taps,
+						 ctx->st1_addexp);
+			if (ret)
+				goto disable_mem_clk;
+
+			ret = write_pf_coeff_mem(easrc, i,
+						 ctx->st2_coeff,
+						 ctx->st2_num_taps,
+						 ctx->st2_addexp);
+			if (ret)
+				goto disable_mem_clk;
+		}
+	}
+
+skip_load:
+	return 0;
+
+disable_mem_clk:
+	clk_disable_unprepare(easrc->mem_clk);
+	return ret;
+}
+#endif /*CONFIG_PM*/
+
+static const struct dev_pm_ops fsl_easrc_pm_ops = {
+	SET_RUNTIME_PM_OPS(fsl_easrc_runtime_suspend,
+			   fsl_easrc_runtime_resume,
+			   NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
+};
+
+static struct platform_driver fsl_easrc_driver = {
+	.probe = fsl_easrc_probe,
+	.remove = fsl_easrc_remove,
+	.driver = {
+		.name = "fsl-easrc",
+		.pm = &fsl_easrc_pm_ops,
+		.of_match_table = fsl_easrc_dt_ids,
+	},
+};
+module_platform_driver(fsl_easrc_driver);
+
+MODULE_DESCRIPTION("NXP Enhanced Asynchronous Sample Rate (eASRC) driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h
new file mode 100644
index 000000000000..205f6ef3e1e3
--- /dev/null
+++ b/sound/soc/fsl/fsl_easrc.h
@@ -0,0 +1,668 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 NXP
+ */
+
+#ifndef _FSL_EASRC_H
+#define _FSL_EASRC_H
+
+#include <sound/asound.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_data/dma-imx.h>
+
+#include "fsl_asrc_common.h"
+
+/* EASRC Register Map */
+
+/* ASRC Input Write FIFO */
+#define REG_EASRC_WRFIFO(ctx)		(0x000 + 4 * (ctx))
+/* ASRC Output Read FIFO */
+#define REG_EASRC_RDFIFO(ctx)		(0x010 + 4 * (ctx))
+/* ASRC Context Control */
+#define REG_EASRC_CC(ctx)		(0x020 + 4 * (ctx))
+/* ASRC Context Control Extended 1 */
+#define REG_EASRC_CCE1(ctx)		(0x030 + 4 * (ctx))
+/* ASRC Context Control Extended 2 */
+#define REG_EASRC_CCE2(ctx)		(0x040 + 4 * (ctx))
+/* ASRC Control Input Access */
+#define REG_EASRC_CIA(ctx)		(0x050 + 4 * (ctx))
+/* ASRC Datapath Processor Control Slot0 */
+#define REG_EASRC_DPCS0R0(ctx)		(0x060 + 4 * (ctx))
+#define REG_EASRC_DPCS0R1(ctx)		(0x070 + 4 * (ctx))
+#define REG_EASRC_DPCS0R2(ctx)		(0x080 + 4 * (ctx))
+#define REG_EASRC_DPCS0R3(ctx)		(0x090 + 4 * (ctx))
+/* ASRC Datapath Processor Control Slot1 */
+#define REG_EASRC_DPCS1R0(ctx)		(0x0A0 + 4 * (ctx))
+#define REG_EASRC_DPCS1R1(ctx)		(0x0B0 + 4 * (ctx))
+#define REG_EASRC_DPCS1R2(ctx)		(0x0C0 + 4 * (ctx))
+#define REG_EASRC_DPCS1R3(ctx)		(0x0D0 + 4 * (ctx))
+/* ASRC Context Output Control */
+#define REG_EASRC_COC(ctx)		(0x0E0 + 4 * (ctx))
+/* ASRC Control Output Access */
+#define REG_EASRC_COA(ctx)		(0x0F0 + 4 * (ctx))
+/* ASRC Sample FIFO Status */
+#define REG_EASRC_SFS(ctx)		(0x100 + 4 * (ctx))
+/* ASRC Resampling Ratio Low */
+#define REG_EASRC_RRL(ctx)		(0x110 + 8 * (ctx))
+/* ASRC Resampling Ratio High */
+#define REG_EASRC_RRH(ctx)		(0x114 + 8 * (ctx))
+/* ASRC Resampling Ratio Update Control */
+#define REG_EASRC_RUC(ctx)		(0x130 + 4 * (ctx))
+/* ASRC Resampling Ratio Update Rate */
+#define REG_EASRC_RUR(ctx)		(0x140 + 4 * (ctx))
+/* ASRC Resampling Center Tap Coefficient Low */
+#define REG_EASRC_RCTCL			(0x150)
+/* ASRC Resampling Center Tap Coefficient High */
+#define REG_EASRC_RCTCH			(0x154)
+/* ASRC Prefilter Coefficient FIFO */
+#define REG_EASRC_PCF(ctx)		(0x160 + 4 * (ctx))
+/* ASRC Context Resampling Coefficient Memory */
+#define REG_EASRC_CRCM			0x170
+/* ASRC Context Resampling Coefficient Control*/
+#define REG_EASRC_CRCC			0x174
+/* ASRC Interrupt Control */
+#define REG_EASRC_IRQC			0x178
+/* ASRC Interrupt Status Flags */
+#define REG_EASRC_IRQF			0x17C
+/* ASRC Channel Status 0 */
+#define REG_EASRC_CS0(ctx)		(0x180 + 4 * (ctx))
+/* ASRC Channel Status 1 */
+#define REG_EASRC_CS1(ctx)		(0x190 + 4 * (ctx))
+/* ASRC Channel Status 2 */
+#define REG_EASRC_CS2(ctx)		(0x1A0 + 4 * (ctx))
+/* ASRC Channel Status 3 */
+#define REG_EASRC_CS3(ctx)		(0x1B0 + 4 * (ctx))
+/* ASRC Channel Status 4 */
+#define REG_EASRC_CS4(ctx)		(0x1C0 + 4 * (ctx))
+/* ASRC Channel Status 5 */
+#define REG_EASRC_CS5(ctx)		(0x1D0 + 4 * (ctx))
+/* ASRC Debug Control Register */
+#define REG_EASRC_DBGC			0x1E0
+/* ASRC Debug Status Register */
+#define REG_EASRC_DBGS			0x1E4
+
+#define REG_EASRC_FIFO(x, ctx)		(x == IN ? REG_EASRC_WRFIFO(ctx) \
+						: REG_EASRC_RDFIFO(ctx))
+
+/* ASRC Context Control (CC) */
+#define EASRC_CC_EN_SHIFT		31
+#define EASRC_CC_EN_MASK		BIT(EASRC_CC_EN_SHIFT)
+#define EASRC_CC_EN			BIT(EASRC_CC_EN_SHIFT)
+#define EASRC_CC_STOP_SHIFT		29
+#define EASRC_CC_STOP_MASK		BIT(EASRC_CC_STOP_SHIFT)
+#define EASRC_CC_STOP			BIT(EASRC_CC_STOP_SHIFT)
+#define EASRC_CC_FWMDE_SHIFT		28
+#define EASRC_CC_FWMDE_MASK		BIT(EASRC_CC_FWMDE_SHIFT)
+#define EASRC_CC_FWMDE			BIT(EASRC_CC_FWMDE_SHIFT)
+#define EASRC_CC_FIFO_WTMK_SHIFT	16
+#define EASRC_CC_FIFO_WTMK_WIDTH	7
+#define EASRC_CC_FIFO_WTMK_MASK		((BIT(EASRC_CC_FIFO_WTMK_WIDTH) - 1) \
+					 << EASRC_CC_FIFO_WTMK_SHIFT)
+#define EASRC_CC_FIFO_WTMK(v)		(((v) << EASRC_CC_FIFO_WTMK_SHIFT) \
+					 & EASRC_CC_FIFO_WTMK_MASK)
+#define EASRC_CC_SAMPLE_POS_SHIFT	11
+#define EASRC_CC_SAMPLE_POS_WIDTH	5
+#define EASRC_CC_SAMPLE_POS_MASK	((BIT(EASRC_CC_SAMPLE_POS_WIDTH) - 1) \
+					 << EASRC_CC_SAMPLE_POS_SHIFT)
+#define EASRC_CC_SAMPLE_POS(v)		(((v) << EASRC_CC_SAMPLE_POS_SHIFT) \
+					 & EASRC_CC_SAMPLE_POS_MASK)
+#define EASRC_CC_ENDIANNESS_SHIFT	10
+#define EASRC_CC_ENDIANNESS_MASK	BIT(EASRC_CC_ENDIANNESS_SHIFT)
+#define EASRC_CC_ENDIANNESS		BIT(EASRC_CC_ENDIANNESS_SHIFT)
+#define EASRC_CC_BPS_SHIFT		8
+#define EASRC_CC_BPS_WIDTH		2
+#define EASRC_CC_BPS_MASK		((BIT(EASRC_CC_BPS_WIDTH) - 1) \
+					 << EASRC_CC_BPS_SHIFT)
+#define EASRC_CC_BPS(v)			(((v) << EASRC_CC_BPS_SHIFT) \
+					 & EASRC_CC_BPS_MASK)
+#define EASRC_CC_FMT_SHIFT		7
+#define EASRC_CC_FMT_MASK		BIT(EASRC_CC_FMT_SHIFT)
+#define EASRC_CC_FMT			BIT(EASRC_CC_FMT_SHIFT)
+#define EASRC_CC_INSIGN_SHIFT		6
+#define EASRC_CC_INSIGN_MASK		BIT(EASRC_CC_INSIGN_SHIFT)
+#define EASRC_CC_INSIGN			BIT(EASRC_CC_INSIGN_SHIFT)
+#define EASRC_CC_CHEN_SHIFT		0
+#define EASRC_CC_CHEN_WIDTH		5
+#define EASRC_CC_CHEN_MASK		((BIT(EASRC_CC_CHEN_WIDTH) - 1) \
+					 << EASRC_CC_CHEN_SHIFT)
+#define EASRC_CC_CHEN(v)		(((v) << EASRC_CC_CHEN_SHIFT) \
+					 & EASRC_CC_CHEN_MASK)
+
+/* ASRC Context Control Extended 1 (CCE1) */
+#define EASRC_CCE1_COEF_WS_SHIFT	25
+#define EASRC_CCE1_COEF_WS_MASK		BIT(EASRC_CCE1_COEF_WS_SHIFT)
+#define EASRC_CCE1_COEF_WS		BIT(EASRC_CCE1_COEF_WS_SHIFT)
+#define EASRC_CCE1_COEF_MEM_RST_SHIFT	24
+#define EASRC_CCE1_COEF_MEM_RST_MASK	BIT(EASRC_CCE1_COEF_MEM_RST_SHIFT)
+#define EASRC_CCE1_COEF_MEM_RST		BIT(EASRC_CCE1_COEF_MEM_RST_SHIFT)
+#define EASRC_CCE1_PF_EXP_SHIFT		16
+#define EASRC_CCE1_PF_EXP_WIDTH		8
+#define EASRC_CCE1_PF_EXP_MASK		((BIT(EASRC_CCE1_PF_EXP_WIDTH) - 1) \
+					 << EASRC_CCE1_PF_EXP_SHIFT)
+#define EASRC_CCE1_PF_EXP(v)		(((v) << EASRC_CCE1_PF_EXP_SHIFT) \
+					 & EASRC_CCE1_PF_EXP_MASK)
+#define EASRC_CCE1_PF_ST1_WBFP_SHIFT	9
+#define EASRC_CCE1_PF_ST1_WBFP_MASK	BIT(EASRC_CCE1_PF_ST1_WBFP_SHIFT)
+#define EASRC_CCE1_PF_ST1_WBFP		BIT(EASRC_CCE1_PF_ST1_WBFP_SHIFT)
+#define EASRC_CCE1_PF_TSEN_SHIFT	8
+#define EASRC_CCE1_PF_TSEN_MASK		BIT(EASRC_CCE1_PF_TSEN_SHIFT)
+#define EASRC_CCE1_PF_TSEN		BIT(EASRC_CCE1_PF_TSEN_SHIFT)
+#define EASRC_CCE1_RS_BYPASS_SHIFT	7
+#define EASRC_CCE1_RS_BYPASS_MASK	BIT(EASRC_CCE1_RS_BYPASS_SHIFT)
+#define EASRC_CCE1_RS_BYPASS		BIT(EASRC_CCE1_RS_BYPASS_SHIFT)
+#define EASRC_CCE1_PF_BYPASS_SHIFT	6
+#define EASRC_CCE1_PF_BYPASS_MASK	BIT(EASRC_CCE1_PF_BYPASS_SHIFT)
+#define EASRC_CCE1_PF_BYPASS		BIT(EASRC_CCE1_PF_BYPASS_SHIFT)
+#define EASRC_CCE1_RS_STOP_SHIFT	5
+#define EASRC_CCE1_RS_STOP_MASK		BIT(EASRC_CCE1_RS_STOP_SHIFT)
+#define EASRC_CCE1_RS_STOP		BIT(EASRC_CCE1_RS_STOP_SHIFT)
+#define EASRC_CCE1_PF_STOP_SHIFT	4
+#define EASRC_CCE1_PF_STOP_MASK		BIT(EASRC_CCE1_PF_STOP_SHIFT)
+#define EASRC_CCE1_PF_STOP		BIT(EASRC_CCE1_PF_STOP_SHIFT)
+#define EASRC_CCE1_RS_INIT_SHIFT	2
+#define EASRC_CCE1_RS_INIT_WIDTH	2
+#define EASRC_CCE1_RS_INIT_MASK		((BIT(EASRC_CCE1_RS_INIT_WIDTH) - 1) \
+					 << EASRC_CCE1_RS_INIT_SHIFT)
+#define EASRC_CCE1_RS_INIT(v)		(((v) << EASRC_CCE1_RS_INIT_SHIFT) \
+					 & EASRC_CCE1_RS_INIT_MASK)
+#define EASRC_CCE1_PF_INIT_SHIFT	0
+#define EASRC_CCE1_PF_INIT_WIDTH	2
+#define EASRC_CCE1_PF_INIT_MASK		((BIT(EASRC_CCE1_PF_INIT_WIDTH) - 1) \
+					 << EASRC_CCE1_PF_INIT_SHIFT)
+#define EASRC_CCE1_PF_INIT(v)		(((v) << EASRC_CCE1_PF_INIT_SHIFT) \
+					 & EASRC_CCE1_PF_INIT_MASK)
+
+/* ASRC Context Control Extended 2 (CCE2) */
+#define EASRC_CCE2_ST2_TAPS_SHIFT	16
+#define EASRC_CCE2_ST2_TAPS_WIDTH	9
+#define EASRC_CCE2_ST2_TAPS_MASK	((BIT(EASRC_CCE2_ST2_TAPS_WIDTH) - 1) \
+					 << EASRC_CCE2_ST2_TAPS_SHIFT)
+#define EASRC_CCE2_ST2_TAPS(v)		(((v) << EASRC_CCE2_ST2_TAPS_SHIFT) \
+					 & EASRC_CCE2_ST2_TAPS_MASK)
+#define EASRC_CCE2_ST1_TAPS_SHIFT	0
+#define EASRC_CCE2_ST1_TAPS_WIDTH	9
+#define EASRC_CCE2_ST1_TAPS_MASK	((BIT(EASRC_CCE2_ST1_TAPS_WIDTH) - 1) \
+					 << EASRC_CCE2_ST1_TAPS_SHIFT)
+#define EASRC_CCE2_ST1_TAPS(v)		(((v) << EASRC_CCE2_ST1_TAPS_SHIFT) \
+					 & EASRC_CCE2_ST1_TAPS_MASK)
+
+/* ASRC Control Input Access (CIA) */
+#define EASRC_CIA_ITER_SHIFT		16
+#define EASRC_CIA_ITER_WIDTH		6
+#define EASRC_CIA_ITER_MASK		((BIT(EASRC_CIA_ITER_WIDTH) - 1) \
+					 << EASRC_CIA_ITER_SHIFT)
+#define EASRC_CIA_ITER(v)		(((v) << EASRC_CIA_ITER_SHIFT) \
+					 & EASRC_CIA_ITER_MASK)
+#define EASRC_CIA_GRLEN_SHIFT		8
+#define EASRC_CIA_GRLEN_WIDTH		6
+#define EASRC_CIA_GRLEN_MASK		((BIT(EASRC_CIA_GRLEN_WIDTH) - 1) \
+					 << EASRC_CIA_GRLEN_SHIFT)
+#define EASRC_CIA_GRLEN(v)		(((v) << EASRC_CIA_GRLEN_SHIFT) \
+					 & EASRC_CIA_GRLEN_MASK)
+#define EASRC_CIA_ACCLEN_SHIFT		0
+#define EASRC_CIA_ACCLEN_WIDTH		6
+#define EASRC_CIA_ACCLEN_MASK		((BIT(EASRC_CIA_ACCLEN_WIDTH) - 1) \
+					 << EASRC_CIA_ACCLEN_SHIFT)
+#define EASRC_CIA_ACCLEN(v)		(((v) << EASRC_CIA_ACCLEN_SHIFT) \
+					 & EASRC_CIA_ACCLEN_MASK)
+
+/* ASRC Datapath Processor Control Slot0 Register0 (DPCS0R0) */
+#define EASRC_DPCS0R0_MAXCH_SHIFT	24
+#define EASRC_DPCS0R0_MAXCH_WIDTH	5
+#define EASRC_DPCS0R0_MAXCH_MASK	((BIT(EASRC_DPCS0R0_MAXCH_WIDTH) - 1) \
+					 << EASRC_DPCS0R0_MAXCH_SHIFT)
+#define EASRC_DPCS0R0_MAXCH(v)		(((v) << EASRC_DPCS0R0_MAXCH_SHIFT) \
+					 & EASRC_DPCS0R0_MAXCH_MASK)
+#define EASRC_DPCS0R0_MINCH_SHIFT	16
+#define EASRC_DPCS0R0_MINCH_WIDTH	5
+#define EASRC_DPCS0R0_MINCH_MASK	((BIT(EASRC_DPCS0R0_MINCH_WIDTH) - 1) \
+					 << EASRC_DPCS0R0_MINCH_SHIFT)
+#define EASRC_DPCS0R0_MINCH(v)		(((v) << EASRC_DPCS0R0_MINCH_SHIFT) \
+					 & EASRC_DPCS0R0_MINCH_MASK)
+#define EASRC_DPCS0R0_NUMCH_SHIFT	8
+#define EASRC_DPCS0R0_NUMCH_WIDTH	5
+#define EASRC_DPCS0R0_NUMCH_MASK	((BIT(EASRC_DPCS0R0_NUMCH_WIDTH) - 1) \
+					 << EASRC_DPCS0R0_NUMCH_SHIFT)
+#define EASRC_DPCS0R0_NUMCH(v)		(((v) << EASRC_DPCS0R0_NUMCH_SHIFT) \
+					 & EASRC_DPCS0R0_NUMCH_MASK)
+#define EASRC_DPCS0R0_CTXNUM_SHIFT	1
+#define EASRC_DPCS0R0_CTXNUM_WIDTH	2
+#define EASRC_DPCS0R0_CTXNUM_MASK	((BIT(EASRC_DPCS0R0_CTXNUM_WIDTH) - 1) \
+					 << EASRC_DPCS0R0_CTXNUM_SHIFT)
+#define EASRC_DPCS0R0_CTXNUM(v)		(((v) << EASRC_DPCS0R0_CTXNUM_SHIFT) \
+					 & EASRC_DPCS0R0_CTXNUM_MASK)
+#define EASRC_DPCS0R0_EN_SHIFT		0
+#define EASRC_DPCS0R0_EN_MASK		BIT(EASRC_DPCS0R0_EN_SHIFT)
+#define EASRC_DPCS0R0_EN		BIT(EASRC_DPCS0R0_EN_SHIFT)
+
+/* ASRC Datapath Processor Control Slot0 Register1 (DPCS0R1) */
+#define EASRC_DPCS0R1_ST1_EXP_SHIFT	0
+#define EASRC_DPCS0R1_ST1_EXP_WIDTH	13
+#define EASRC_DPCS0R1_ST1_EXP_MASK	((BIT(EASRC_DPCS0R1_ST1_EXP_WIDTH) - 1) \
+					 << EASRC_DPCS0R1_ST1_EXP_SHIFT)
+#define EASRC_DPCS0R1_ST1_EXP(v)	(((v) << EASRC_DPCS0R1_ST1_EXP_SHIFT) \
+					 & EASRC_DPCS0R1_ST1_EXP_MASK)
+
+/* ASRC Datapath Processor Control Slot0 Register2 (DPCS0R2) */
+#define EASRC_DPCS0R2_ST1_MA_SHIFT	16
+#define EASRC_DPCS0R2_ST1_MA_WIDTH	13
+#define EASRC_DPCS0R2_ST1_MA_MASK	((BIT(EASRC_DPCS0R2_ST1_MA_WIDTH) - 1) \
+					 << EASRC_DPCS0R2_ST1_MA_SHIFT)
+#define EASRC_DPCS0R2_ST1_MA(v)		(((v) << EASRC_DPCS0R2_ST1_MA_SHIFT) \
+					 & EASRC_DPCS0R2_ST1_MA_MASK)
+#define EASRC_DPCS0R2_ST1_SA_SHIFT	0
+#define EASRC_DPCS0R2_ST1_SA_WIDTH	13
+#define EASRC_DPCS0R2_ST1_SA_MASK	((BIT(EASRC_DPCS0R2_ST1_SA_WIDTH) - 1) \
+					 << EASRC_DPCS0R2_ST1_SA_SHIFT)
+#define EASRC_DPCS0R2_ST1_SA(v)		(((v) << EASRC_DPCS0R2_ST1_SA_SHIFT) \
+					 & EASRC_DPCS0R2_ST1_SA_MASK)
+
+/* ASRC Datapath Processor Control Slot0 Register3 (DPCS0R3) */
+#define EASRC_DPCS0R3_ST2_MA_SHIFT	16
+#define EASRC_DPCS0R3_ST2_MA_WIDTH	13
+#define EASRC_DPCS0R3_ST2_MA_MASK	((BIT(EASRC_DPCS0R3_ST2_MA_WIDTH) - 1) \
+					 << EASRC_DPCS0R3_ST2_MA_SHIFT)
+#define EASRC_DPCS0R3_ST2_MA(v)		(((v) << EASRC_DPCS0R3_ST2_MA_SHIFT) \
+					 & EASRC_DPCS0R3_ST2_MA_MASK)
+#define EASRC_DPCS0R3_ST2_SA_SHIFT	0
+#define EASRC_DPCS0R3_ST2_SA_WIDTH	13
+#define EASRC_DPCS0R3_ST2_SA_MASK	((BIT(EASRC_DPCS0R3_ST2_SA_WIDTH) - 1) \
+					 << EASRC_DPCS0R3_ST2_SA_SHIFT)
+#define EASRC_DPCS0R3_ST2_SA(v)		(((v) << EASRC_DPCS0R3_ST2_SA_SHIFT) \
+						 & EASRC_DPCS0R3_ST2_SA_MASK)
+
+/* ASRC Context Output Control (COC) */
+#define EASRC_COC_FWMDE_SHIFT		28
+#define EASRC_COC_FWMDE_MASK		BIT(EASRC_COC_FWMDE_SHIFT)
+#define EASRC_COC_FWMDE			BIT(EASRC_COC_FWMDE_SHIFT)
+#define EASRC_COC_FIFO_WTMK_SHIFT	16
+#define EASRC_COC_FIFO_WTMK_WIDTH	7
+#define EASRC_COC_FIFO_WTMK_MASK	((BIT(EASRC_COC_FIFO_WTMK_WIDTH) - 1) \
+					 << EASRC_COC_FIFO_WTMK_SHIFT)
+#define EASRC_COC_FIFO_WTMK(v)		(((v) << EASRC_COC_FIFO_WTMK_SHIFT) \
+					 & EASRC_COC_FIFO_WTMK_MASK)
+#define EASRC_COC_SAMPLE_POS_SHIFT	11
+#define EASRC_COC_SAMPLE_POS_WIDTH	5
+#define EASRC_COC_SAMPLE_POS_MASK	((BIT(EASRC_COC_SAMPLE_POS_WIDTH) - 1) \
+					 << EASRC_COC_SAMPLE_POS_SHIFT)
+#define EASRC_COC_SAMPLE_POS(v)		(((v) << EASRC_COC_SAMPLE_POS_SHIFT) \
+					 & EASRC_COC_SAMPLE_POS_MASK)
+#define EASRC_COC_ENDIANNESS_SHIFT	10
+#define EASRC_COC_ENDIANNESS_MASK	BIT(EASRC_COC_ENDIANNESS_SHIFT)
+#define EASRC_COC_ENDIANNESS		BIT(EASRC_COC_ENDIANNESS_SHIFT)
+#define EASRC_COC_BPS_SHIFT		8
+#define EASRC_COC_BPS_WIDTH		2
+#define EASRC_COC_BPS_MASK		((BIT(EASRC_COC_BPS_WIDTH) - 1) \
+					 << EASRC_COC_BPS_SHIFT)
+#define EASRC_COC_BPS(v)		(((v) << EASRC_COC_BPS_SHIFT) \
+					 & EASRC_COC_BPS_MASK)
+#define EASRC_COC_FMT_SHIFT		7
+#define EASRC_COC_FMT_MASK		BIT(EASRC_COC_FMT_SHIFT)
+#define EASRC_COC_FMT			BIT(EASRC_COC_FMT_SHIFT)
+#define EASRC_COC_OUTSIGN_SHIFT		6
+#define EASRC_COC_OUTSIGN_MASK		BIT(EASRC_COC_OUTSIGN_SHIFT)
+#define EASRC_COC_OUTSIGN_OUT		BIT(EASRC_COC_OUTSIGN_SHIFT)
+#define EASRC_COC_IEC_VDATA_SHIFT	2
+#define EASRC_COC_IEC_VDATA_MASK	BIT(EASRC_COC_IEC_VDATA_SHIFT)
+#define EASRC_COC_IEC_VDATA		BIT(EASRC_COC_IEC_VDATA_SHIFT)
+#define EASRC_COC_IEC_EN_SHIFT		1
+#define EASRC_COC_IEC_EN_MASK		BIT(EASRC_COC_IEC_EN_SHIFT)
+#define EASRC_COC_IEC_EN		BIT(EASRC_COC_IEC_EN_SHIFT)
+#define EASRC_COC_DITHER_EN_SHIFT	0
+#define EASRC_COC_DITHER_EN_MASK	BIT(EASRC_COC_DITHER_EN_SHIFT)
+#define EASRC_COC_DITHER_EN		BIT(EASRC_COC_DITHER_EN_SHIFT)
+
+/* ASRC Control Output Access (COA) */
+#define EASRC_COA_ITER_SHIFT		16
+#define EASRC_COA_ITER_WIDTH		6
+#define EASRC_COA_ITER_MASK		((BIT(EASRC_COA_ITER_WIDTH) - 1) \
+					 << EASRC_COA_ITER_SHIFT)
+#define EASRC_COA_ITER(v)		(((v) << EASRC_COA_ITER_SHIFT) \
+					 & EASRC_COA_ITER_MASK)
+#define EASRC_COA_GRLEN_SHIFT		8
+#define EASRC_COA_GRLEN_WIDTH		6
+#define EASRC_COA_GRLEN_MASK		((BIT(EASRC_COA_GRLEN_WIDTH) - 1) \
+					 << EASRC_COA_GRLEN_SHIFT)
+#define EASRC_COA_GRLEN(v)		(((v) << EASRC_COA_GRLEN_SHIFT) \
+					 & EASRC_COA_GRLEN_MASK)
+#define EASRC_COA_ACCLEN_SHIFT		0
+#define EASRC_COA_ACCLEN_WIDTH		6
+#define EASRC_COA_ACCLEN_MASK		((BIT(EASRC_COA_ACCLEN_WIDTH) - 1) \
+					 << EASRC_COA_ACCLEN_SHIFT)
+#define EASRC_COA_ACCLEN(v)		(((v) << EASRC_COA_ACCLEN_SHIFT) \
+					 & EASRC_COA_ACCLEN_MASK)
+
+/* ASRC Sample FIFO Status (SFS) */
+#define EASRC_SFS_IWTMK_SHIFT		23
+#define EASRC_SFS_IWTMK_MASK		BIT(EASRC_SFS_IWTMK_SHIFT)
+#define EASRC_SFS_IWTMK			BIT(EASRC_SFS_IWTMK_SHIFT)
+#define EASRC_SFS_NSGI_SHIFT		16
+#define EASRC_SFS_NSGI_WIDTH		7
+#define EASRC_SFS_NSGI_MASK		((BIT(EASRC_SFS_NSGI_WIDTH) - 1) \
+					 << EASRC_SFS_NSGI_SHIFT)
+#define EASRC_SFS_NSGI(v)		(((v) << EASRC_SFS_NSGI_SHIFT) \
+					 & EASRC_SFS_NSGI_MASK)
+#define EASRC_SFS_OWTMK_SHIFT		7
+#define EASRC_SFS_OWTMK_MASK		BIT(EASRC_SFS_OWTMK_SHIFT)
+#define EASRC_SFS_OWTMK			BIT(EASRC_SFS_OWTMK_SHIFT)
+#define EASRC_SFS_NSGO_SHIFT		0
+#define EASRC_SFS_NSGO_WIDTH		7
+#define EASRC_SFS_NSGO_MASK		((BIT(EASRC_SFS_NSGO_WIDTH) - 1) \
+					 << EASRC_SFS_NSGO_SHIFT)
+#define EASRC_SFS_NSGO(v)		(((v) << EASRC_SFS_NSGO_SHIFT) \
+					 & EASRC_SFS_NSGO_MASK)
+
+/* ASRC Resampling Ratio Low (RRL) */
+#define EASRC_RRL_RS_RL_SHIFT		0
+#define EASRC_RRL_RS_RL_WIDTH		32
+#define EASRC_RRL_RS_RL(v)		((v) << EASRC_RRL_RS_RL_SHIFT)
+
+/* ASRC Resampling Ratio High (RRH) */
+#define EASRC_RRH_RS_VLD_SHIFT		31
+#define EASRC_RRH_RS_VLD_MASK		BIT(EASRC_RRH_RS_VLD_SHIFT)
+#define EASRC_RRH_RS_VLD		BIT(EASRC_RRH_RS_VLD_SHIFT)
+#define EASRC_RRH_RS_RH_SHIFT		0
+#define EASRC_RRH_RS_RH_WIDTH		12
+#define EASRC_RRH_RS_RH_MASK		((BIT(EASRC_RRH_RS_RH_WIDTH) - 1) \
+					 << EASRC_RRH_RS_RH_SHIFT)
+#define EASRC_RRH_RS_RH(v)		(((v) << EASRC_RRH_RS_RH_SHIFT) \
+					 & EASRC_RRH_RS_RH_MASK)
+
+/* ASRC Resampling Ratio Update Control (RSUC) */
+#define EASRC_RSUC_RS_RM_SHIFT		0
+#define EASRC_RSUC_RS_RM_WIDTH		32
+#define EASRC_RSUC_RS_RM(v)		((v) << EASRC_RSUC_RS_RM_SHIFT)
+
+/* ASRC Resampling Ratio Update Rate (RRUR) */
+#define EASRC_RRUR_RRR_SHIFT		0
+#define EASRC_RRUR_RRR_WIDTH		31
+#define EASRC_RRUR_RRR_MASK		((BIT(EASRC_RRUR_RRR_WIDTH) - 1) \
+					 << EASRC_RRUR_RRR_SHIFT)
+#define EASRC_RRUR_RRR(v)		(((v) << EASRC_RRUR_RRR_SHIFT) \
+					 & EASRC_RRUR_RRR_MASK)
+
+/* ASRC Resampling Center Tap Coefficient Low (RCTCL) */
+#define EASRC_RCTCL_RS_CL_SHIFT		0
+#define EASRC_RCTCL_RS_CL_WIDTH		32
+#define EASRC_RCTCL_RS_CL(v)		((v) << EASRC_RCTCL_RS_CL_SHIFT)
+
+/* ASRC Resampling Center Tap Coefficient High (RCTCH) */
+#define EASRC_RCTCH_RS_CH_SHIFT		0
+#define EASRC_RCTCH_RS_CH_WIDTH		32
+#define EASRC_RCTCH_RS_CH(v)		((v) << EASRC_RCTCH_RS_CH_SHIFT)
+
+/* ASRC Prefilter Coefficient FIFO (PCF) */
+#define EASRC_PCF_CD_SHIFT		0
+#define EASRC_PCF_CD_WIDTH		32
+#define EASRC_PCF_CD(v)			((v) << EASRC_PCF_CD_SHIFT)
+
+/* ASRC Context Resampling Coefficient Memory (CRCM) */
+#define EASRC_CRCM_RS_CWD_SHIFT		0
+#define EASRC_CRCM_RS_CWD_WIDTH		32
+#define EASRC_CRCM_RS_CWD(v)		((v) << EASRC_CRCM_RS_CWD_SHIFT)
+
+/* ASRC Context Resampling Coefficient Control (CRCC) */
+#define EASRC_CRCC_RS_CA_SHIFT		16
+#define EASRC_CRCC_RS_CA_WIDTH		11
+#define EASRC_CRCC_RS_CA_MASK		((BIT(EASRC_CRCC_RS_CA_WIDTH) - 1) \
+					 << EASRC_CRCC_RS_CA_SHIFT)
+#define EASRC_CRCC_RS_CA(v)		(((v) << EASRC_CRCC_RS_CA_SHIFT) \
+					 & EASRC_CRCC_RS_CA_MASK)
+#define EASRC_CRCC_RS_TAPS_SHIFT	1
+#define EASRC_CRCC_RS_TAPS_WIDTH	2
+#define EASRC_CRCC_RS_TAPS_MASK		((BIT(EASRC_CRCC_RS_TAPS_WIDTH) - 1) \
+					 << EASRC_CRCC_RS_TAPS_SHIFT)
+#define EASRC_CRCC_RS_TAPS(v)		(((v) << EASRC_CRCC_RS_TAPS_SHIFT) \
+					 & EASRC_CRCC_RS_TAPS_MASK)
+#define EASRC_CRCC_RS_CPR_SHIFT		0
+#define EASRC_CRCC_RS_CPR_MASK		BIT(EASRC_CRCC_RS_CPR_SHIFT)
+#define EASRC_CRCC_RS_CPR		BIT(EASRC_CRCC_RS_CPR_SHIFT)
+
+/* ASRC Interrupt_Control (IC) */
+#define EASRC_IRQC_RSDM_SHIFT		8
+#define EASRC_IRQC_RSDM_WIDTH		4
+#define EASRC_IRQC_RSDM_MASK		((BIT(EASRC_IRQC_RSDM_WIDTH) - 1) \
+					 << EASRC_IRQC_RSDM_SHIFT)
+#define EASRC_IRQC_RSDM(v)		(((v) << EASRC_IRQC_RSDM_SHIFT) \
+					 & EASRC_IRQC_RSDM_MASK)
+#define EASRC_IRQC_OERM_SHIFT		4
+#define EASRC_IRQC_OERM_WIDTH		4
+#define EASRC_IRQC_OERM_MASK		((BIT(EASRC_IRQC_OERM_WIDTH) - 1) \
+					 << EASRC_IRQC_OERM_SHIFT)
+#define EASRC_IRQC_OERM(v)		(((v) << EASRC_IRQC_OERM_SHIFT) \
+					 & EASRC_IEQC_OERM_MASK)
+#define EASRC_IRQC_IOM_SHIFT		0
+#define EASRC_IRQC_IOM_WIDTH		4
+#define EASRC_IRQC_IOM_MASK		((BIT(EASRC_IRQC_IOM_WIDTH) - 1) \
+					 << EASRC_IRQC_IOM_SHIFT)
+#define EASRC_IRQC_IOM(v)		(((v) << EASRC_IRQC_IOM_SHIFT) \
+					 & EASRC_IRQC_IOM_MASK)
+
+/* ASRC Interrupt Status Flags (ISF) */
+#define EASRC_IRQF_RSD_SHIFT		8
+#define EASRC_IRQF_RSD_WIDTH		4
+#define EASRC_IRQF_RSD_MASK		((BIT(EASRC_IRQF_RSD_WIDTH) - 1) \
+					 << EASRC_IRQF_RSD_SHIFT)
+#define EASRC_IRQF_RSD(v)		(((v) << EASRC_IRQF_RSD_SHIFT) \
+					 & EASRC_IRQF_RSD_MASK)
+#define EASRC_IRQF_OER_SHIFT		4
+#define EASRC_IRQF_OER_WIDTH		4
+#define EASRC_IRQF_OER_MASK		((BIT(EASRC_IRQF_OER_WIDTH) - 1) \
+					 << EASRC_IRQF_OER_SHIFT)
+#define EASRC_IRQF_OER(v)		(((v) << EASRC_IRQF_OER_SHIFT) \
+					 & EASRC_IRQF_OER_MASK)
+#define EASRC_IRQF_IFO_SHIFT		0
+#define EASRC_IRQF_IFO_WIDTH		4
+#define EASRC_IRQF_IFO_MASK		((BIT(EASRC_IRQF_IFO_WIDTH) - 1) \
+					 << EASRC_IRQF_IFO_SHIFT)
+#define EASRC_IRQF_IFO(v)		(((v) << EASRC_IRQF_IFO_SHIFT) \
+					 & EASRC_IRQF_IFO_MASK)
+
+/* ASRC Context Channel STAT */
+#define EASRC_CSx_CSx_SHIFT		0
+#define EASRC_CSx_CSx_WIDTH		32
+#define EASRC_CSx_CSx(v)		((v) << EASRC_CSx_CSx_SHIFT)
+
+/* ASRC Debug Control Register */
+#define EASRC_DBGC_DMS_SHIFT		0
+#define EASRC_DBGC_DMS_WIDTH		6
+#define EASRC_DBGC_DMS_MASK		((BIT(EASRC_DBGC_DMS_WIDTH) - 1) \
+					 << EASRC_DBGC_DMS_SHIFT)
+#define EASRC_DBGC_DMS(v)		(((v) << EASRC_DBGC_DMS_SHIFT) \
+					 & EASRC_DBGC_DMS_MASK)
+
+/* ASRC Debug Status Register */
+#define EASRC_DBGS_DS_SHIFT		0
+#define EASRC_DBGS_DS_WIDTH		32
+#define EASRC_DBGS_DS(v)		((v) << EASRC_DBGS_DS_SHIFT)
+
+/* General Constants */
+#define EASRC_CTX_MAX_NUM		4
+#define EASRC_RS_COEFF_MEM		0
+#define EASRC_PF_COEFF_MEM		1
+
+/* Prefilter constants */
+#define EASRC_PF_ST1_ONLY		0
+#define EASRC_PF_TWO_STAGE_MODE		1
+#define EASRC_PF_ST1_COEFF_WR		0
+#define EASRC_PF_ST2_COEFF_WR		1
+#define EASRC_MAX_PF_TAPS		384
+
+/* Resampling constants */
+#define EASRC_RS_32_TAPS		0
+#define EASRC_RS_64_TAPS		1
+#define EASRC_RS_128_TAPS		2
+
+/* Initialization mode */
+#define EASRC_INIT_MODE_SW_CONTROL	0
+#define EASRC_INIT_MODE_REPLICATE	1
+#define EASRC_INIT_MODE_ZERO_FILL	2
+
+/* FIFO watermarks */
+#define FSL_EASRC_INPUTFIFO_WML		0x4
+#define FSL_EASRC_OUTPUTFIFO_WML	0x1
+
+#define EASRC_INPUTFIFO_THRESHOLD_MIN	0
+#define EASRC_INPUTFIFO_THRESHOLD_MAX	127
+#define EASRC_OUTPUTFIFO_THRESHOLD_MIN	0
+#define EASRC_OUTPUTFIFO_THRESHOLD_MAX	63
+
+#define EASRC_DMA_BUFFER_SIZE		(1024 * 48 * 9)
+#define EASRC_MAX_BUFFER_SIZE		(1024 * 48)
+
+#define FIRMWARE_MAGIC			0xDEAD
+#define FIRMWARE_VERSION		1
+
+enum easrc_word_width {
+	EASRC_WIDTH_16_BIT = 0,
+	EASRC_WIDTH_20_BIT = 1,
+	EASRC_WIDTH_24_BIT = 2,
+	EASRC_WIDTH_32_BIT = 3,
+};
+
+struct __attribute__((__packed__))  asrc_firmware_hdr {
+	u32 magic;
+	u32 interp_scen;
+	u32 prefil_scen;
+	u32 firmware_version;
+};
+
+struct __attribute__((__packed__)) interp_params {
+	u32 magic;
+	u32 num_taps;
+	u32 num_phases;
+	u64 center_tap;
+	u64 coeff[8192];
+};
+
+struct __attribute__((__packed__)) prefil_params {
+	u32 magic;
+	u32 insr;
+	u32 outsr;
+	u32 st1_taps;
+	u32 st2_taps;
+	u32 st1_exp;
+	u64 coeff[256];
+};
+
+struct dma_block {
+	void *dma_vaddr;
+	unsigned int length;
+	unsigned int max_buf_size;
+};
+
+struct fsl_easrc_data_fmt {
+	unsigned int width : 2;
+	unsigned int endianness : 1;
+	unsigned int unsign : 1;
+	unsigned int floating_point : 1;
+	unsigned int iec958: 1;
+	unsigned int sample_pos: 5;
+	unsigned int addexp;
+};
+
+struct fsl_easrc_io_params {
+	struct fsl_easrc_data_fmt fmt;
+	unsigned int group_len;
+	unsigned int iterations;
+	unsigned int access_len;
+	unsigned int fifo_wtmk;
+	unsigned int sample_rate;
+	unsigned int sample_format;
+	unsigned int norm_rate;
+};
+
+struct fsl_easrc_slot {
+	bool busy;
+	int ctx_index;
+	int num_channel;  /*maximum is 8*/
+	int min_channel;
+	int max_channel;
+	int pf_mem_used;
+};
+
+struct fsl_easrc_context {
+	enum asrc_pair_index index;
+	struct fsl_easrc *easrc;
+	struct dma_chan *dma_chan[2];
+	struct dma_async_tx_descriptor *desc[2];
+	struct fsl_easrc_io_params in_params;
+	struct fsl_easrc_io_params out_params;
+	struct imx_dma_data dma_data;
+	unsigned int channels;
+	unsigned int st1_num_taps;
+	unsigned int st2_num_taps;
+	unsigned int st1_num_exp;
+	unsigned int pf_init_mode;
+	unsigned int rs_init_mode;
+	unsigned int ctx_streams;
+	unsigned int pos;
+	u64 rs_ratio;
+	u64 *st1_coeff;
+	u64 *st2_coeff;
+	int in_filled_sample;
+	int out_missed_sample;
+	int st1_addexp;
+	int st2_addexp;
+	void *private_data;
+};
+
+/**
+ * fsl_easrc: EASRC private data
+ *
+ * @pdev: platform device pointer
+ * @regmap: regmap handler
+ * @dma_params_rx: DMA parameters for receive channel
+ * @dma_params_tx: DMA parameters for transmit channel
+ * @ctx:  context pointer
+ * @slot: slot setting
+ * @mem_clk: clock source to access register
+ * @firmware_hdr:  the header of firmware
+ * @interp: pointer to interpolation filter coeff
+ * @prefil: pointer to prefilter coeff
+ * @fw: firmware of coeff table
+ * @fw_name: firmware name
+ * @paddr: physical address to the base address of registers
+ * @rs_num_taps:  resample filter taps, 32, 64, or 128
+ * @bps_i2c958: bits per sample of iec958
+ * @chn_avail: available channels, maximum 32
+ * @lock: spin lock for resource protection
+ * @easrc_rate: default sample rate for ASoC Back-Ends
+ * @easrc_format: default sample format for ASoC Back-Ends
+ */
+
+struct fsl_easrc {
+	struct platform_device *pdev;
+	struct regmap *regmap;
+	struct miscdevice easrc_miscdev;
+	struct snd_dmaengine_dai_dma_data dma_params_rx;
+	struct snd_dmaengine_dai_dma_data dma_params_tx;
+	struct fsl_easrc_context *ctx[EASRC_CTX_MAX_NUM];
+	struct fsl_easrc_slot slot[EASRC_CTX_MAX_NUM][2];
+	struct clk *mem_clk;
+	struct asrc_firmware_hdr *firmware_hdr;
+	struct interp_params *interp;
+	struct prefil_params *prefil;
+	const struct firmware *fw;
+	const char *fw_name;
+	unsigned long paddr;
+	unsigned int rs_num_taps;
+	unsigned int bps_iec958[EASRC_CTX_MAX_NUM];
+	unsigned int chn_avail;
+	u64 *rs_coeff;
+	u64 const_coeff;
+	int firmware_loaded;
+	spinlock_t lock;  /* spin lock for resource protection */
+	int easrc_rate;
+	int easrc_format;
+};
+
+extern const struct snd_soc_component_driver fsl_easrc_dma_component;
+struct dma_chan *fsl_easrc_get_dma_channel(struct fsl_easrc_context *ctx,
+					   bool dir);
+int fsl_easrc_request_context(struct fsl_easrc_context *ctx,
+			      unsigned int channels);
+int fsl_easrc_release_context(struct fsl_easrc_context *ctx);
+
+#define DRV_NAME "fsl-easrc-dma"
+#endif /* _FSL_EASRC_H */
diff --git a/sound/soc/fsl/fsl_easrc_dma.c b/sound/soc/fsl/fsl_easrc_dma.c
new file mode 100644
index 000000000000..1f5a89053412
--- /dev/null
+++ b/sound/soc/fsl/fsl_easrc_dma.c
@@ -0,0 +1,440 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2019 NXP
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/platform_data/dma-imx.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_easrc.h"
+
+#define FSL_ASRC_DMABUF_SIZE	(256 * 1024)
+
+static struct snd_pcm_hardware snd_imx_hardware = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.buffer_bytes_max = FSL_ASRC_DMABUF_SIZE,
+	.period_bytes_min = 128,
+	.period_bytes_max = 65532, /* Limited by SDMA engine */
+	.periods_min = 2,
+	.periods_max = 255,
+	.fifo_size = 0,
+};
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+	if (!imx_dma_is_general_purpose(chan))
+		return false;
+
+	chan->private = param;
+
+	return true;
+}
+
+static void fsl_easrc_dma_complete(void *arg)
+{
+	struct snd_pcm_substream *substream = arg;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_easrc_context *ctx = runtime->private_data;
+
+	ctx->pos += snd_pcm_lib_period_bytes(substream);
+	if (ctx->pos >= snd_pcm_lib_buffer_bytes(substream))
+		ctx->pos = 0;
+
+	snd_pcm_period_elapsed(substream);
+}
+
+static int fsl_easrc_dma_prepare_and_submit(struct snd_pcm_substream *substream,
+					    struct snd_soc_component *component)
+{
+	u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_easrc_context *ctx = runtime->private_data;
+	struct device *dev = component->dev;
+	unsigned long flags = DMA_CTRL_ACK;
+
+	/* Prepare and submit Front-End DMA channel */
+	if (!substream->runtime->no_period_wakeup)
+		flags |= DMA_PREP_INTERRUPT;
+
+	ctx->pos = 0;
+	ctx->desc[!dir] = dmaengine_prep_dma_cyclic(ctx->dma_chan[!dir],
+						    runtime->dma_addr,
+						    snd_pcm_lib_buffer_bytes(substream),
+						    snd_pcm_lib_period_bytes(substream),
+						    dir == OUT ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+						    flags);
+	if (!ctx->desc[!dir]) {
+		dev_err(dev, "failed to prepare slave DMA for Front-End\n");
+		return -ENOMEM;
+	}
+
+	ctx->desc[!dir]->callback = fsl_easrc_dma_complete;
+	ctx->desc[!dir]->callback_param = substream;
+
+	dmaengine_submit(ctx->desc[!dir]);
+
+	/* Prepare and submit Back-End DMA channel */
+	ctx->desc[dir] = dmaengine_prep_dma_cyclic(ctx->dma_chan[dir],
+						   0xffff, 64, 64,
+						   DMA_DEV_TO_DEV, 0);
+	if (!ctx->desc[dir]) {
+		dev_err(dev, "failed to prepare slave DMA for Back-End\n");
+		return -ENOMEM;
+	}
+
+	dmaengine_submit(ctx->desc[dir]);
+
+	return 0;
+}
+
+static int fsl_easrc_dma_trigger(struct snd_soc_component *component,
+				 struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_easrc_context *ctx = runtime->private_data;
+	int ret;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ret = fsl_easrc_dma_prepare_and_submit(substream, component);
+		if (ret)
+			return ret;
+		dma_async_issue_pending(ctx->dma_chan[IN]);
+		dma_async_issue_pending(ctx->dma_chan[OUT]);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		dmaengine_terminate_all(ctx->dma_chan[OUT]);
+		dmaengine_terminate_all(ctx->dma_chan[IN]);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int fsl_easrc_dma_hw_params(struct snd_soc_component *component,
+				   struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *params)
+{
+	enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+	struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL;
+	struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_easrc_context *ctx = runtime->private_data;
+	struct fsl_easrc *easrc = ctx->easrc;
+	struct dma_slave_config config_fe, config_be;
+	enum asrc_pair_index index = ctx->index;
+	struct device *dev = component->dev;
+	int stream = substream->stream;
+	struct imx_dma_data *tmp_data;
+	struct snd_soc_dpcm *dpcm;
+	struct dma_chan *tmp_chan;
+	struct device *dev_be;
+	u8 dir = tx ? OUT : IN;
+	dma_cap_mask_t mask;
+	int ret;
+
+	/* Fetch the Back-End dma_data from DPCM */
+	list_for_each_entry(dpcm, &rtd->dpcm[stream].be_clients, list_be) {
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_pcm_substream *substream_be;
+		struct snd_soc_dai *dai = be->cpu_dai;
+
+		if (dpcm->fe != rtd)
+			continue;
+
+		substream_be = snd_soc_dpcm_get_substream(be, stream);
+		dma_params_be = snd_soc_dai_get_dma_data(dai, substream_be);
+		dev_be = dai->dev;
+		break;
+	}
+
+	if (!dma_params_be) {
+		dev_err(dev, "failed to get the substream of Back-End\n");
+		return -EINVAL;
+	}
+
+	/* Override dma_data of the Front-End and config its dmaengine */
+	dma_params_fe = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+	dma_params_fe->addr = easrc->paddr + REG_EASRC_FIFO(!dir, index);
+	dma_params_fe->maxburst = dma_params_be->maxburst;
+
+	ctx->dma_chan[!dir] = fsl_easrc_get_dma_channel(ctx, !dir);
+	if (!ctx->dma_chan[!dir]) {
+		dev_err(dev, "failed to request DMA channel\n");
+		return -EINVAL;
+	}
+
+	memset(&config_fe, 0, sizeof(config_fe));
+	ret = snd_dmaengine_pcm_prepare_slave_config(substream,
+						     params, &config_fe);
+	if (ret) {
+		dev_err(dev, "failed to prepare DMA config for Front-End\n");
+		return ret;
+	}
+
+	ret = dmaengine_slave_config(ctx->dma_chan[!dir], &config_fe);
+	if (ret) {
+		dev_err(dev, "failed to config DMA channel for Front-End\n");
+		return ret;
+	}
+
+	/* Request and config DMA channel for Back-End */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	dma_cap_set(DMA_CYCLIC, mask);
+
+	/* Get DMA request of Back-End */
+	tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
+	tmp_data = tmp_chan->private;
+	ctx->dma_data.dma_request = tmp_data->dma_request;
+	dma_release_channel(tmp_chan);
+
+	/* Get DMA request of Front-End */
+	tmp_chan = fsl_easrc_get_dma_channel(ctx, dir);
+	tmp_data = tmp_chan->private;
+	ctx->dma_data.dma_request2 = tmp_data->dma_request;
+	ctx->dma_data.peripheral_type = tmp_data->peripheral_type;
+	ctx->dma_data.priority = tmp_data->priority;
+	dma_release_channel(tmp_chan);
+
+	ctx->dma_chan[dir] = dma_request_channel(mask, filter, &ctx->dma_data);
+	if (!ctx->dma_chan[dir]) {
+		dev_err(dev, "failed to request DMA channel for Back-End\n");
+		return -EINVAL;
+	}
+
+	if (easrc->easrc_format == SNDRV_PCM_FORMAT_S16_LE)
+		buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+	else
+		buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	memset(&config_be, 0, sizeof(config_be));
+
+	config_be.direction = DMA_DEV_TO_DEV;
+	config_be.src_addr_width = buswidth;
+	config_be.src_maxburst = dma_params_be->maxburst;
+	config_be.dst_addr_width = buswidth;
+	config_be.dst_maxburst = dma_params_be->maxburst;
+
+	if (tx) {
+		config_be.src_addr = easrc->paddr + REG_EASRC_RDFIFO(index);
+		config_be.dst_addr = dma_params_be->addr;
+	} else {
+		config_be.dst_addr = easrc->paddr + REG_EASRC_WRFIFO(index);
+		config_be.src_addr = dma_params_be->addr;
+	}
+
+	ret = dmaengine_slave_config(ctx->dma_chan[dir], &config_be);
+	if (ret) {
+		dev_err(dev, "failed to config DMA channel for Back-End\n");
+		return ret;
+	}
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	return 0;
+}
+
+static int fsl_easrc_dma_hw_free(struct snd_soc_component *component,
+				 struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_easrc_context *ctx = runtime->private_data;
+
+	snd_pcm_set_runtime_buffer(substream, NULL);
+
+	if (ctx->dma_chan[IN])
+		dma_release_channel(ctx->dma_chan[IN]);
+
+	if (ctx->dma_chan[OUT])
+		dma_release_channel(ctx->dma_chan[OUT]);
+
+	ctx->dma_chan[IN] = NULL;
+	ctx->dma_chan[OUT] = NULL;
+
+	return 0;
+}
+
+static int fsl_easrc_dma_startup(struct snd_soc_component *component,
+				 struct snd_pcm_substream *substream)
+{
+	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dmaengine_dai_dma_data *dma_data;
+	struct device *dev = component->dev;
+	struct fsl_easrc *easrc = dev_get_drvdata(dev);
+	struct fsl_easrc_context *ctx;
+	struct dma_chan *tmp_chan = NULL;
+	u8 dir = tx ? OUT : IN;
+	bool release_pair = true;
+	int ret = 0;
+
+	ret = snd_pcm_hw_constraint_integer(substream->runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0) {
+		dev_err(dev, "failed to set pcm hw params periods\n");
+		return ret;
+	}
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->easrc = easrc;
+
+	runtime->private_data = ctx;
+
+	/* Request a dummy pair, which will be released later.
+	 * Request pair function needs channel num as input, for this
+	 * dummy pair, we just request "1" channel temporarily.
+	 */
+	fsl_easrc_request_context(ctx, 1);
+	if (ret < 0) {
+		dev_err(dev, "failed to request asrc pair\n");
+		goto req_pair_err;
+	}
+
+	/* Request a dummy dma channel, which will be released later. */
+	tmp_chan = fsl_easrc_get_dma_channel(ctx, dir);
+	if (!tmp_chan) {
+		dev_err(dev, "failed to get dma channel\n");
+		ret = -EINVAL;
+		goto dma_chan_err;
+	}
+
+	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+	/* Refine the snd_imx_hardware according to caps of DMA. */
+	ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream,
+							dma_data,
+							&snd_imx_hardware,
+							tmp_chan);
+	if (ret < 0) {
+		dev_err(dev, "failed to refine runtime hwparams\n");
+		goto out;
+	}
+
+	release_pair = false;
+	snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
+
+out:
+	dma_release_channel(tmp_chan);
+
+dma_chan_err:
+	fsl_easrc_release_context(ctx);
+
+req_pair_err:
+	if (release_pair)
+		kfree(ctx);
+
+	return ret;
+}
+
+static int fsl_easrc_dma_shutdown(struct snd_soc_component *component,
+				  struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_easrc_context *ctx = runtime->private_data;
+	struct fsl_easrc *easrc;
+
+	if (!ctx)
+		return 0;
+
+	easrc = ctx->easrc;
+
+	if (easrc->ctx[ctx->index] == ctx)
+		easrc->ctx[ctx->index] = NULL;
+
+	kfree(ctx);
+
+	return 0;
+}
+
+static snd_pcm_uframes_t fsl_easrc_dma_pcm_pointer(struct snd_soc_component *component,
+						   struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fsl_easrc_context *ctx = runtime->private_data;
+
+	return bytes_to_frames(substream->runtime, ctx->pos);
+}
+
+static int fsl_easrc_dma_pcm_new(struct snd_soc_component *component,
+				 struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm_substream *substream;
+	struct snd_pcm *pcm = rtd->pcm;
+	int ret, i;
+
+	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(card->dev, "failed to set DMA mask\n");
+		return ret;
+	}
+
+	for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
+		substream = pcm->streams[i].substream;
+		if (!substream)
+			continue;
+
+		ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
+					  FSL_ASRC_DMABUF_SIZE,
+					  &substream->dma_buffer);
+		if (ret) {
+			dev_err(card->dev, "failed to allocate DMA buffer\n");
+			goto err;
+		}
+	}
+
+	return 0;
+
+err:
+	if (--i == 0 && pcm->streams[i].substream)
+		snd_dma_free_pages(&pcm->streams[i].substream->dma_buffer);
+
+	return ret;
+}
+
+static void fsl_easrc_dma_pcm_free(struct snd_soc_component *component,
+				   struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	int i;
+
+	for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
+		substream = pcm->streams[i].substream;
+		if (!substream)
+			continue;
+
+		snd_dma_free_pages(&substream->dma_buffer);
+		substream->dma_buffer.area = NULL;
+		substream->dma_buffer.addr = 0;
+	}
+}
+
+const struct snd_soc_component_driver fsl_easrc_dma_component = {
+	.name		= DRV_NAME,
+	.hw_params	= fsl_easrc_dma_hw_params,
+	.hw_free	= fsl_easrc_dma_hw_free,
+	.trigger	= fsl_easrc_dma_trigger,
+	.open		= fsl_easrc_dma_startup,
+	.close		= fsl_easrc_dma_shutdown,
+	.pointer	= fsl_easrc_dma_pcm_pointer,
+	.pcm_construct	= fsl_easrc_dma_pcm_new,
+	.pcm_destruct	= fsl_easrc_dma_pcm_free,
+};
+EXPORT_SYMBOL_GPL(fsl_easrc_dma_component);
-- 
2.21.0


^ permalink raw reply related

* Re: [PATCH kernel v3] powerpc/book3s64: Fix error handling in mm_iommu_do_alloc()
From: Alexey Kardashevskiy @ 2020-02-18  7:08 UTC (permalink / raw)
  To: Michael Ellerman, linuxppc-dev; +Cc: Jan Kara, kvm-ppc, David Gibson
In-Reply-To: <56e98c75-ea44-6805-4bd3-c41620834e9e@ozlabs.ru>



On 24/12/2019 10:32, Alexey Kardashevskiy wrote:
> 
> 
> On 23/12/2019 22:18, Michael Ellerman wrote:
>> Alexey Kardashevskiy <aik@ozlabs.ru> writes:
>>
>>> The last jump to free_exit in mm_iommu_do_alloc() happens after page
>>> pointers in struct mm_iommu_table_group_mem_t were already converted to
>>> physical addresses. Thus calling put_page() on these physical addresses
>>> will likely crash.
>>>
>>> This moves the loop which calculates the pageshift and converts page
>>> struct pointers to physical addresses later after the point when
>>> we cannot fail; thus eliminating the need to convert pointers back.
>>>
>>> Fixes: eb9d7a62c386 ("powerpc/mm_iommu: Fix potential deadlock")
>>> Reported-by: Jan Kara <jack@suse.cz>
>>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>>> ---
>>> Changes:
>>> v3:
>>> * move pointers conversion after the last possible failure point
>>> ---
>>>  arch/powerpc/mm/book3s64/iommu_api.c | 39 +++++++++++++++-------------
>>>  1 file changed, 21 insertions(+), 18 deletions(-)
>>>
>>> diff --git a/arch/powerpc/mm/book3s64/iommu_api.c b/arch/powerpc/mm/book3s64/iommu_api.c
>>> index 56cc84520577..ef164851738b 100644
>>> --- a/arch/powerpc/mm/book3s64/iommu_api.c
>>> +++ b/arch/powerpc/mm/book3s64/iommu_api.c
>>> @@ -121,24 +121,6 @@ static long mm_iommu_do_alloc(struct mm_struct *mm, unsigned long ua,
>>>  		goto free_exit;
>>>  	}
>>>  
>>> -	pageshift = PAGE_SHIFT;
>>> -	for (i = 0; i < entries; ++i) {
>>> -		struct page *page = mem->hpages[i];
>>> -
>>> -		/*
>>> -		 * Allow to use larger than 64k IOMMU pages. Only do that
>>> -		 * if we are backed by hugetlb.
>>> -		 */
>>> -		if ((mem->pageshift > PAGE_SHIFT) && PageHuge(page))
>>> -			pageshift = page_shift(compound_head(page));
>>> -		mem->pageshift = min(mem->pageshift, pageshift);
>>> -		/*
>>> -		 * We don't need struct page reference any more, switch
>>> -		 * to physical address.
>>> -		 */
>>> -		mem->hpas[i] = page_to_pfn(page) << PAGE_SHIFT;
>>> -	}
>>> -
>>>  good_exit:
>>>  	atomic64_set(&mem->mapped, 1);
>>>  	mem->used = 1;
>>> @@ -158,6 +140,27 @@ static long mm_iommu_do_alloc(struct mm_struct *mm, unsigned long ua,
>>>  		}
>>>  	}
>>>  
>>> +	if (mem->dev_hpa == MM_IOMMU_TABLE_INVALID_HPA) {
>>
>> Couldn't you avoid testing this again ...
>>
>>> +		/*
>>> +		 * Allow to use larger than 64k IOMMU pages. Only do that
>>> +		 * if we are backed by hugetlb. Skip device memory as it is not
>>> +		 * backed with page structs.
>>> +		 */
>>> +		pageshift = PAGE_SHIFT;
>>> +		for (i = 0; i < entries; ++i) {
>>
>> ... by making this loop up to `pinned`.
>>
>> `pinned` is only incremented in the loop that does the GUP, and there's
>> a check that pinned == entries after that loop.
>>
>> So when we get here we know pinned == entries, and if pinned is zero
>> it's because we took the (dev_hpa != MM_IOMMU_TABLE_INVALID_HPA) case at
>> the start of the function to get here.
>>
>> Or do you think that's too subtle to rely on?
> 
> 
> I had 4 choices:
> 
> 1. for (;i < pinned;)
> 
> 2. if (dev_hpa == MM_IOMMU_TABLE_INVALID_HPA) (dev_hpa is a function
> parameter)
> 
> 3. if (mem->dev_hpa == MM_IOMMU_TABLE_INVALID_HPA)
> 
> 4. if (mem->hpages)
> 
> 
> The function is already ugly. 3) seemed as the most obvious way of
> telling what is going on here: "we have just initialized @mem and it is
> not for a device memory, lets finish the initialization".
> 
> I could rearrange the code even more but since there is no NVLink3
> coming ever, I'd avoid changing it more than necessary. Thanks,



Repost? Rework?



> 
> 
>>
>> cheers
>>
>>> +			struct page *page = mem->hpages[i];
>>> +
>>> +			if ((mem->pageshift > PAGE_SHIFT) && PageHuge(page))
>>> +				pageshift = page_shift(compound_head(page));
>>> +			mem->pageshift = min(mem->pageshift, pageshift);
>>> +			/*
>>> +			 * We don't need struct page reference any more, switch
>>> +			 * to physical address.
>>> +			 */
>>> +			mem->hpas[i] = page_to_pfn(page) << PAGE_SHIFT;
>>> +		}
>>> +	}
>>> +
>>>  	list_add_rcu(&mem->next, &mm->context.iommu_group_mem_list);
>>>  
>>>  	mutex_unlock(&mem_list_mutex);
>>> -- 
>>> 2.17.1
> 

-- 
Alexey

^ permalink raw reply

* [PATCH kernel 3/5] powerpc/powernv/ioda: Allow smaller TCE table levels
From: Alexey Kardashevskiy @ 2020-02-18  7:36 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: Alexey Kardashevskiy, Alistair Popple, Alex Williamson, kvm-ppc,
	David Gibson
In-Reply-To: <20200218073650.16149-1-aik@ozlabs.ru>

Now the minimum allocation size for a TCE table level is PAGE_SIZE (64k)
as this is the minimum for alloc_pages(). The limit was set in POWER8
where we did not have sparse RAM so we did not need sparse TCE tables.
On POWER9 we have gaps in the phys address space for which using multi
level TCE tables makes sense. The problem with that is that 64K per level
is too much for 2 levels and 1GB pages as it exceeds the hardware limit
of 55bits so we need smaller levels.

This drops the minimum level size to 4K.

For a machine with 2 CPUs, top RAM address 0x4000.0000.0000
(each node gets 32TiB) and 1GiB IOMMU pages:
Before the patch: 512KiB or 8 pages.
After the patch: 3 pages: one level1 + 2xlevel2 tables, each can map
up to 64k>>3<<30 = 8TiB of physical space.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
 arch/powerpc/platforms/powernv/pci-ioda-tce.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/pci-ioda-tce.c b/arch/powerpc/platforms/powernv/pci-ioda-tce.c
index 5dc6847d5f4c..82e680da9d94 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda-tce.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda-tce.c
@@ -37,7 +37,7 @@ static __be64 *pnv_alloc_tce_level(int nid, unsigned int shift)
 	__be64 *addr;
 
 	tce_mem = alloc_pages_node(nid, GFP_ATOMIC | __GFP_NOWARN,
-			shift - PAGE_SHIFT);
+			shift > PAGE_SHIFT ? shift - PAGE_SHIFT : 0);
 	if (!tce_mem) {
 		pr_err("Failed to allocate a TCE memory, level shift=%d\n",
 				shift);
@@ -282,7 +282,7 @@ long pnv_pci_ioda2_table_alloc_pages(int nid, __u64 bus_offset,
 	/* Adjust direct table size from window_size and levels */
 	entries_shift = (entries_shift + levels - 1) / levels;
 	level_shift = entries_shift + 3;
-	level_shift = max_t(unsigned int, level_shift, PAGE_SHIFT);
+	level_shift = max_t(unsigned int, level_shift, 12); /* 4K is minimum */
 
 	if ((level_shift - 3) * levels + page_shift >= 55)
 		return -EINVAL;
-- 
2.17.1


^ permalink raw reply related

* [PATCH kernel 0/5] powerpc/powenv/ioda: Allow huge DMA window at 4GB
From: Alexey Kardashevskiy @ 2020-02-18  7:36 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: Alexey Kardashevskiy, Alistair Popple, Alex Williamson, kvm-ppc,
	David Gibson


Here is an attempt to support bigger DMA space for devices
supporting DMA masks less than 59 bits (GPUs come into mind
first). POWER9 PHBs have an option to map 2 windows at 0
and select a windows based on DMA address being below or above
4GB.

This adds the "iommu=iommu_bypass" kernel parameter and
supports VFIO+pseries machine - current this requires telling
upstream+unmodified QEMU about this via
-global spapr-pci-host-bridge.dma64_win_addr=0x100000000
or per-phb property. 4/4 advertises the new option but
there is no automation around it in QEMU (should it be?).

For now it is either 1<<59 or 4GB mode; dynamic switching is
not supported (could be via sysfs).

This is a rebased version of
https://lore.kernel.org/kvm/20191202015953.127902-1-aik@ozlabs.ru/

This is based on sha1
71c3a888cbca Linus Torvalds "Merge tag 'powerpc-5.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux".

Please comment. Thanks.



Alexey Kardashevskiy (5):
  powerpc/powernv/ioda: Move TCE bypass base to PE
  powerpc/powernv/ioda: Rework for huge DMA window at 4GB
  powerpc/powernv/ioda: Allow smaller TCE table levels
  powerpc/powernv/phb4: Add 4GB IOMMU bypass mode
  vfio/spapr_tce: Advertise and allow a huge DMA windows at 4GB

 arch/powerpc/include/asm/iommu.h              |   2 +
 arch/powerpc/include/asm/opal-api.h           |   9 +-
 arch/powerpc/include/asm/opal.h               |   2 +
 arch/powerpc/platforms/powernv/pci.h          |   2 +-
 include/uapi/linux/vfio.h                     |   2 +
 arch/powerpc/platforms/powernv/npu-dma.c      |   1 +
 arch/powerpc/platforms/powernv/opal-call.c    |   2 +
 arch/powerpc/platforms/powernv/pci-ioda-tce.c |   4 +-
 arch/powerpc/platforms/powernv/pci-ioda.c     | 229 ++++++++++++++----
 drivers/vfio/vfio_iommu_spapr_tce.c           |  10 +-
 10 files changed, 207 insertions(+), 56 deletions(-)

-- 
2.17.1


^ permalink raw reply

* [PATCH kernel 1/5] powerpc/powernv/ioda: Move TCE bypass base to PE
From: Alexey Kardashevskiy @ 2020-02-18  7:36 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: Alexey Kardashevskiy, Alistair Popple, Alex Williamson, kvm-ppc,
	David Gibson
In-Reply-To: <20200218073650.16149-1-aik@ozlabs.ru>

We are about to allow another location for the second DMA window and
we will need to advertise it outside of the powernv platform code.

This moves bypass base address to iommu_table_group so drivers such as
VFIO SPAPR TCE can see it.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
 arch/powerpc/include/asm/iommu.h          |  1 +
 arch/powerpc/platforms/powernv/pci.h      |  1 -
 arch/powerpc/platforms/powernv/npu-dma.c  |  1 +
 arch/powerpc/platforms/powernv/pci-ioda.c | 26 ++++++++++-------------
 4 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
index 350101e11ddb..479439ef003e 100644
--- a/arch/powerpc/include/asm/iommu.h
+++ b/arch/powerpc/include/asm/iommu.h
@@ -194,6 +194,7 @@ struct iommu_table_group {
 	__u64 pgsizes; /* Bitmap of supported page sizes */
 	__u32 max_dynamic_windows_supported;
 	__u32 max_levels;
+	__u64 tce64_start;
 
 	struct iommu_group *group;
 	struct iommu_table *tables[IOMMU_TABLE_GROUP_MAX_TABLES];
diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h
index d3bbdeab3a32..a808dd396522 100644
--- a/arch/powerpc/platforms/powernv/pci.h
+++ b/arch/powerpc/platforms/powernv/pci.h
@@ -67,7 +67,6 @@ struct pnv_ioda_pe {
 
 	/* 64-bit TCE bypass region */
 	bool			tce_bypass_enabled;
-	uint64_t		tce_bypass_base;
 
 	/* MSIs. MVE index is identical for for 32 and 64 bit MSI
 	 * and -1 if not supported. (It's actually identical to the
diff --git a/arch/powerpc/platforms/powernv/npu-dma.c b/arch/powerpc/platforms/powernv/npu-dma.c
index b95b9e3c4c98..97a479848003 100644
--- a/arch/powerpc/platforms/powernv/npu-dma.c
+++ b/arch/powerpc/platforms/powernv/npu-dma.c
@@ -469,6 +469,7 @@ struct iommu_table_group *pnv_try_setup_npu_table_group(struct pnv_ioda_pe *pe)
 	table_group->tce32_start = pe->table_group.tce32_start;
 	table_group->tce32_size = pe->table_group.tce32_size;
 	table_group->max_levels = pe->table_group.max_levels;
+	table_group->tce64_start = pe->table_group.tce64_start;
 	if (!table_group->pgsizes)
 		table_group->pgsizes = pe->table_group.pgsizes;
 
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 22c22cd7bd82..52db10ab4fef 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -1777,7 +1777,7 @@ static void pnv_pci_ioda_dma_dev_setup(struct pci_dev *pdev)
 
 	pe = &phb->ioda.pe_array[pdn->pe_number];
 	WARN_ON(get_dma_ops(&pdev->dev) != &dma_iommu_ops);
-	pdev->dev.archdata.dma_offset = pe->tce_bypass_base;
+	pdev->dev.archdata.dma_offset = pe->table_group.tce64_start;
 	set_iommu_table_base(&pdev->dev, pe->table_group.tables[0]);
 	/*
 	 * Note: iommu_add_device() will fail here as
@@ -1869,7 +1869,8 @@ static bool pnv_pci_ioda_iommu_bypass_supported(struct pci_dev *pdev,
 
 	pe = &phb->ioda.pe_array[pdn->pe_number];
 	if (pe->tce_bypass_enabled) {
-		u64 top = pe->tce_bypass_base + memblock_end_of_DRAM() - 1;
+		u64 top = pe->table_group.tce64_start +
+			memblock_end_of_DRAM() - 1;
 		if (dma_mask >= top)
 			return true;
 	}
@@ -1903,7 +1904,7 @@ static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe, struct pci_bus *bus)
 
 	list_for_each_entry(dev, &bus->devices, bus_list) {
 		set_iommu_table_base(&dev->dev, pe->table_group.tables[0]);
-		dev->dev.archdata.dma_offset = pe->tce_bypass_base;
+		dev->dev.archdata.dma_offset = pe->table_group.tce64_start;
 
 		if ((pe->flags & PNV_IODA_PE_BUS_ALL) && dev->subordinate)
 			pnv_ioda_setup_bus_dma(pe, dev->subordinate);
@@ -2361,16 +2362,12 @@ static void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable)
 
 		top = roundup_pow_of_two(top);
 		rc = opal_pci_map_pe_dma_window_real(pe->phb->opal_id,
-						     pe->pe_number,
-						     window_id,
-						     pe->tce_bypass_base,
-						     top);
+				pe->pe_number, window_id,
+				pe->table_group.tce64_start, top);
 	} else {
 		rc = opal_pci_map_pe_dma_window_real(pe->phb->opal_id,
-						     pe->pe_number,
-						     window_id,
-						     pe->tce_bypass_base,
-						     0);
+				pe->pe_number, window_id,
+				pe->table_group.tce64_start, 0);
 	}
 	if (rc)
 		pe_err(pe, "OPAL error %lld configuring bypass window\n", rc);
@@ -2385,7 +2382,8 @@ static long pnv_pci_ioda2_create_table(struct iommu_table_group *table_group,
 	struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe,
 			table_group);
 	int nid = pe->phb->hose->node;
-	__u64 bus_offset = num ? pe->tce_bypass_base : table_group->tce32_start;
+	__u64 bus_offset = num ?
+		pe->table_group.tce64_start : table_group->tce32_start;
 	long ret;
 	struct iommu_table *tbl;
 
@@ -2735,9 +2733,6 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,
 	if (!pnv_pci_ioda_pe_dma_weight(pe))
 		return;
 
-	/* TVE #1 is selected by PCI address bit 59 */
-	pe->tce_bypass_base = 1ull << 59;
-
 	/* The PE will reserve all possible 32-bits space */
 	pe_info(pe, "Setting up 32-bit TCE table at 0..%08x\n",
 		phb->ioda.m32_pci_base);
@@ -2745,6 +2740,7 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,
 	/* Setup linux iommu table */
 	pe->table_group.tce32_start = 0;
 	pe->table_group.tce32_size = phb->ioda.m32_pci_base;
+	pe->table_group.tce64_start = 1UL << 59;
 	pe->table_group.max_dynamic_windows_supported =
 			IOMMU_TABLE_GROUP_MAX_TABLES;
 	pe->table_group.max_levels = POWERNV_IOMMU_MAX_LEVELS;
-- 
2.17.1


^ permalink raw reply related

* [PATCH kernel 2/5] powerpc/powernv/ioda: Rework for huge DMA window at 4GB
From: Alexey Kardashevskiy @ 2020-02-18  7:36 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: Alexey Kardashevskiy, Alistair Popple, Alex Williamson, kvm-ppc,
	David Gibson
In-Reply-To: <20200218073650.16149-1-aik@ozlabs.ru>

This moves code to make the next patches look simpler. In particular:

1. Separate locals declaration as we will be allocating a smaller DMA
window if a TVE1_4GB option (allows a huge DMA windows at 4GB) is enabled;

2. Pass the bypass offset directly to pnv_pci_ioda2_create_table()
as it is the only information needed from @pe;

3. Use PAGE_SHIFT for it_map allocation estimate and @tceshift for
the IOMMU page size; this makes the distinction clear and allows
easy switching between different IOMMU page size.

These changes should not cause behavioral change.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---

I really need 1), 2) makes the code less dependent on the PE struct member
value (==easier to follow), 3) is to enable 2MB quickly for the default
DMA window for debugging/performance testing.
---
 arch/powerpc/platforms/powernv/pci-ioda.c | 38 ++++++++++++-----------
 1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 52db10ab4fef..f5f1b4e25530 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -2375,15 +2375,10 @@ static void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable)
 		pe->tce_bypass_enabled = enable;
 }
 
-static long pnv_pci_ioda2_create_table(struct iommu_table_group *table_group,
-		int num, __u32 page_shift, __u64 window_size, __u32 levels,
+static long pnv_pci_ioda2_create_table(int nid, int num, __u64 bus_offset,
+		__u32 page_shift, __u64 window_size, __u32 levels,
 		bool alloc_userspace_copy, struct iommu_table **ptbl)
 {
-	struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe,
-			table_group);
-	int nid = pe->phb->hose->node;
-	__u64 bus_offset = num ?
-		pe->table_group.tce64_start : table_group->tce32_start;
 	long ret;
 	struct iommu_table *tbl;
 
@@ -2410,21 +2405,23 @@ static long pnv_pci_ioda2_setup_default_config(struct pnv_ioda_pe *pe)
 {
 	struct iommu_table *tbl = NULL;
 	long rc;
-	unsigned long res_start, res_end;
+	u64 max_memory, maxblock, window_size;
+	const unsigned int tceshift = PAGE_SHIFT;
+	unsigned long res_start, res_end, tces_order, tcelevel_order, levels;
 
 	/*
 	 * crashkernel= specifies the kdump kernel's maximum memory at
 	 * some offset and there is no guaranteed the result is a power
 	 * of 2, which will cause errors later.
 	 */
-	const u64 max_memory = __rounddown_pow_of_two(memory_hotplug_max());
+	max_memory = __rounddown_pow_of_two(memory_hotplug_max());
 
 	/*
 	 * In memory constrained environments, e.g. kdump kernel, the
 	 * DMA window can be larger than available memory, which will
 	 * cause errors later.
 	 */
-	const u64 maxblock = 1UL << (PAGE_SHIFT + MAX_ORDER - 1);
+	maxblock = 1UL << (PAGE_SHIFT + MAX_ORDER - 1);
 
 	/*
 	 * We create the default window as big as we can. The constraint is
@@ -2434,11 +2431,11 @@ static long pnv_pci_ioda2_setup_default_config(struct pnv_ioda_pe *pe)
 	 * to support crippled devices (i.e. not fully 64bit DMAble) only.
 	 */
 	/* iommu_table::it_map uses 1 bit per IOMMU page, hence 8 */
-	const u64 window_size = min((maxblock * 8) << PAGE_SHIFT, max_memory);
+	window_size = min((maxblock * 8) << tceshift, max_memory);
 	/* Each TCE level cannot exceed maxblock so go multilevel if needed */
-	unsigned long tces_order = ilog2(window_size >> PAGE_SHIFT);
-	unsigned long tcelevel_order = ilog2(maxblock >> 3);
-	unsigned int levels = tces_order / tcelevel_order;
+	tces_order = ilog2(window_size >> tceshift);
+	tcelevel_order = ilog2(maxblock >> 3);
+	levels = tces_order / tcelevel_order;
 
 	if (tces_order % tcelevel_order)
 		levels += 1;
@@ -2448,8 +2445,8 @@ static long pnv_pci_ioda2_setup_default_config(struct pnv_ioda_pe *pe)
 	 */
 	levels = max_t(unsigned int, levels, POWERNV_IOMMU_DEFAULT_LEVELS);
 
-	rc = pnv_pci_ioda2_create_table(&pe->table_group, 0, PAGE_SHIFT,
-			window_size, levels, false, &tbl);
+	rc = pnv_pci_ioda2_create_table(pe->phb->hose->node,
+			0, 0, tceshift, window_size, levels, false, &tbl);
 	if (rc) {
 		pe_err(pe, "Failed to create 32-bit TCE table, err %ld",
 				rc);
@@ -2551,8 +2548,13 @@ static long pnv_pci_ioda2_create_table_userspace(
 		int num, __u32 page_shift, __u64 window_size, __u32 levels,
 		struct iommu_table **ptbl)
 {
-	long ret = pnv_pci_ioda2_create_table(table_group,
-			num, page_shift, window_size, levels, true, ptbl);
+	struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe,
+			table_group);
+	__u64 bus_offset = num ?
+		pe->table_group.tce64_start : table_group->tce32_start;
+	long ret = pnv_pci_ioda2_create_table(pe->phb->hose->node,
+			num, bus_offset, page_shift, window_size, levels, true,
+			ptbl);
 
 	if (!ret)
 		(*ptbl)->it_allocated_size = pnv_pci_ioda2_get_table_size(
-- 
2.17.1


^ permalink raw reply related

* [PATCH kernel 4/5] powerpc/powernv/phb4: Add 4GB IOMMU bypass mode
From: Alexey Kardashevskiy @ 2020-02-18  7:36 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: Alexey Kardashevskiy, Alistair Popple, Alex Williamson, kvm-ppc,
	David Gibson
In-Reply-To: <20200218073650.16149-1-aik@ozlabs.ru>

IODA2 systems (POWER8/9) allow DMA windows at 2 fixed locations - 0 and
0x800.0000.0000.0000==1<<59, stored in TVT as TVE0/1. PHB4 on POWER9 has
an additional PHB mode to allow mapping both windows at 0 and selecting
one based on IOBA address - accesses below 4GB go via TVE0 and above
4GB - via TVE1; it is called TVT Select 'GTE4GB' Option in PHB4.

This adds a new value for the "iommu" kernel parameter - "iommu_bypass".
When enabled this programs PHB to enable the TVE1_4GB mode and uses TVE1
to map the entire host RAM using the largest available IOMMU pages
- 1GB for PHB and 256MB for NPU-PHB effectively.

This creates huge DMA windows at 4GB for VFIO; new iommu_table::it_dmaoff
flag tells the VFIO IOMMU driver about the bus offset it should be using.
Since the actual second table still covers the first 4GB of the bus space
iommu_table::it_offset is set to 0. The next patch will explicitly
advertise new offset to the userspace.

When TVE1_4GB enabled, this creates a small (2GB typically) 32 bit window
as there is no need to cover as much of lower DMA space - the 4GB+ window
does it better anyway.

This keeps the existing behavior by default as the TVE1_4GB flag is set
per PHB by device assignment is done on PE basis and managing both modes
dynamically might get nasty.

This relies on skiboot supporting the new OPAL_PHB_SET_OPTION OPAL call.

Suggested-by: Alistair Popple <alistair@popple.id.au>
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
 arch/powerpc/include/asm/iommu.h           |   1 +
 arch/powerpc/include/asm/opal-api.h        |   9 +-
 arch/powerpc/include/asm/opal.h            |   2 +
 arch/powerpc/platforms/powernv/pci.h       |   1 +
 arch/powerpc/platforms/powernv/opal-call.c |   2 +
 arch/powerpc/platforms/powernv/pci-ioda.c  | 187 ++++++++++++++++++---
 6 files changed, 173 insertions(+), 29 deletions(-)

diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
index 479439ef003e..e0ca82d31253 100644
--- a/arch/powerpc/include/asm/iommu.h
+++ b/arch/powerpc/include/asm/iommu.h
@@ -97,6 +97,7 @@ struct iommu_table {
 	unsigned long  it_level_size;
 	unsigned long  it_allocated_size;
 	unsigned long  it_offset;    /* Offset into global table */
+	unsigned long  it_dmaoff;
 	unsigned long  it_base;      /* mapped address of tce table */
 	unsigned long  it_index;     /* which iommu table this is */
 	unsigned long  it_type;      /* type: PCI or Virtual Bus */
diff --git a/arch/powerpc/include/asm/opal-api.h b/arch/powerpc/include/asm/opal-api.h
index c1f25a760eb1..7873754f5ea6 100644
--- a/arch/powerpc/include/asm/opal-api.h
+++ b/arch/powerpc/include/asm/opal-api.h
@@ -214,7 +214,9 @@
 #define OPAL_SECVAR_GET				176
 #define OPAL_SECVAR_GET_NEXT			177
 #define OPAL_SECVAR_ENQUEUE_UPDATE		178
-#define OPAL_LAST				178
+#define OPAL_PHB_SET_OPTION			179
+#define OPAL_PHB_GET_OPTION			180
+#define OPAL_LAST				180
 
 #define QUIESCE_HOLD			1 /* Spin all calls at entry */
 #define QUIESCE_REJECT			2 /* Fail all calls with OPAL_BUSY */
@@ -437,6 +439,11 @@ enum OpalSlotLedState {
 	OPAL_SLOT_LED_STATE_ON = 1	/* LED is ON */
 };
 
+enum OpalPhbOption {
+	OPAL_PHB_OPTION_TVE1_4GB = 0x1,
+	OPAL_PHB_OPTION_MMIO_EEH_DISABLE = 0x2,
+};
+
 /*
  * Address cycle types for LPC accesses. These also correspond
  * to the content of the first cell of the "reg" property for
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 9986ac34b8e2..89b712288cdd 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -142,6 +142,8 @@ int64_t opal_pci_map_pe_dma_window(uint64_t phb_id, uint16_t pe_number, uint16_t
 int64_t opal_pci_map_pe_dma_window_real(uint64_t phb_id, uint16_t pe_number,
 					uint16_t dma_window_number, uint64_t pci_start_addr,
 					uint64_t pci_mem_size);
+int64_t opal_phb_set_option(uint64_t phb_id, uint64_t opt, uint64_t setting);
+int64_t opal_phb_get_option(uint64_t phb_id, uint64_t opt, uint64_t *setting);
 int64_t opal_pci_reset(uint64_t id, uint8_t reset_scope, uint8_t assert_state);
 
 int64_t opal_pci_get_hub_diag_data(uint64_t hub_id, void *diag_buffer,
diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h
index a808dd396522..ce00278185b0 100644
--- a/arch/powerpc/platforms/powernv/pci.h
+++ b/arch/powerpc/platforms/powernv/pci.h
@@ -100,6 +100,7 @@ struct pnv_phb {
 	int			has_dbgfs;
 	struct dentry		*dbgfs;
 #endif
+	struct iommu_table	*bypass_tbl; /* PNV_IOMMU_TCE_BYPASS only */
 
 	unsigned int		msi_base;
 	unsigned int		msi32_support;
diff --git a/arch/powerpc/platforms/powernv/opal-call.c b/arch/powerpc/platforms/powernv/opal-call.c
index 5cd0f52d258f..3130d5a41570 100644
--- a/arch/powerpc/platforms/powernv/opal-call.c
+++ b/arch/powerpc/platforms/powernv/opal-call.c
@@ -293,3 +293,5 @@ OPAL_CALL(opal_mpipl_query_tag,			OPAL_MPIPL_QUERY_TAG);
 OPAL_CALL(opal_secvar_get,			OPAL_SECVAR_GET);
 OPAL_CALL(opal_secvar_get_next,			OPAL_SECVAR_GET_NEXT);
 OPAL_CALL(opal_secvar_enqueue_update,		OPAL_SECVAR_ENQUEUE_UPDATE);
+OPAL_CALL(opal_phb_set_option,			OPAL_PHB_SET_OPTION);
+OPAL_CALL(opal_phb_get_option,			OPAL_PHB_GET_OPTION);
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index f5f1b4e25530..74b4ac8cb71d 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -51,6 +51,10 @@ static const char * const pnv_phb_names[] = { "IODA1", "IODA2", "NPU_NVLINK",
 					      "NPU_OCAPI" };
 
 static void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable);
+static unsigned long pnv_ioda_parse_tce_sizes(struct pnv_phb *phb);
+static long pnv_pci_ioda2_create_table(int nid, int num, __u64 bus_offset,
+		__u32 page_shift, __u64 window_size, __u32 levels,
+		bool alloc_userspace_copy, struct iommu_table **ptbl);
 
 void pe_level_printk(const struct pnv_ioda_pe *pe, const char *level,
 			    const char *fmt, ...)
@@ -83,7 +87,14 @@ void pe_level_printk(const struct pnv_ioda_pe *pe, const char *level,
 	va_end(args);
 }
 
-static bool pnv_iommu_bypass_disabled __read_mostly;
+enum pnv_iommu_bypass_mode {
+	PNV_IOMMU_NO_TRANSLATE,
+	PNV_IOMMU_BYPASS_DISABLED,
+	PNV_IOMMU_TCE_BYPASS
+};
+
+static enum pnv_iommu_bypass_mode pnv_iommu_bypass_mode __read_mostly =
+		PNV_IOMMU_NO_TRANSLATE;
 static bool pci_reset_phbs __read_mostly;
 
 static int __init iommu_setup(char *str)
@@ -93,9 +104,13 @@ static int __init iommu_setup(char *str)
 
 	while (*str) {
 		if (!strncmp(str, "nobypass", 8)) {
-			pnv_iommu_bypass_disabled = true;
+			pnv_iommu_bypass_mode = PNV_IOMMU_BYPASS_DISABLED;
 			pr_info("PowerNV: IOMMU bypass window disabled.\n");
 			break;
+		} else if (!strncmp(str, "iommu_bypass", 12)) {
+			pnv_iommu_bypass_mode = PNV_IOMMU_TCE_BYPASS;
+			pr_info("PowerNV: IOMMU TCE bypass window selected.\n");
+			break;
 		}
 		str += strcspn(str, ",");
 		if (*str == ',')
@@ -2351,28 +2366,125 @@ static long pnv_pci_ioda2_set_window(struct iommu_table_group *table_group,
 	return 0;
 }
 
+static long pnv_pci_ioda2_set_bypass_iommu(struct pnv_ioda_pe *pe,
+		unsigned long offset)
+{
+	struct pnv_phb *phb = pe->phb;
+	long rc;
+	struct memblock_region *r;
+	unsigned long i, pgsizes, tceoff;
+
+
+	pgsizes = pnv_ioda_parse_tce_sizes(phb);
+	/* Filter sizes to have round number of TCEs to cover 0..offset */
+	pgsizes &= offset | (offset - 1);
+	if (!pgsizes)
+		return -1;
+
+	if (!phb->bypass_tbl) {
+		struct iommu_table *tbl = NULL;
+
+		rc = pnv_pci_ioda2_create_table(phb->hose->node,
+				1 /* window number */,
+				0 /* bus offset */,
+				__fls(pgsizes),
+				roundup_pow_of_two(memory_hotplug_max() +
+					offset),
+				2 /* levels */,
+				false /* userspace cache */,
+				&tbl);
+		if (rc)
+			return -1;
+
+		tceoff = offset >> tbl->it_page_shift;
+
+		for_each_memblock(memory, r) {
+			phys_addr_t start, end;
+
+			start = r->base >> tbl->it_page_shift;
+			end = start + (r->size >> tbl->it_page_shift);
+
+			for (i = start; i < end; ++i)
+				tbl->it_ops->set(tbl, i + tceoff, 1,
+						i << tbl->it_page_shift,
+						DMA_BIDIRECTIONAL, 0);
+		}
+		phb->bypass_tbl = tbl;
+		pe_err(pe, "Created 64-bit bypass TCE table at 0x%lx\n",
+			phb->bypass_tbl->it_base);
+	} else {
+		iommu_tce_table_get(phb->bypass_tbl);
+	}
+
+	rc = pnv_pci_ioda2_set_window(&pe->table_group, 1, phb->bypass_tbl);
+	if (rc) {
+		iommu_tce_table_put(phb->bypass_tbl);
+		return -1;
+	}
+
+	pe->tce_bypass_enabled = true;
+
+	return 0;
+}
+
 static void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable)
 {
+	struct pnv_phb *phb = pe->phb;
 	uint16_t window_id = (pe->pe_number << 1 ) + 1;
 	int64_t rc;
+	phys_addr_t top;
 
-	pe_info(pe, "%sabling 64-bit DMA bypass\n", enable ? "En" : "Dis");
-	if (enable) {
-		phys_addr_t top = memblock_end_of_DRAM();
+	if (!enable) {
+		pe_info(pe, "Disabling 64-bit bypass\n");
+		/*
+		 * Whether it was bypass or iommu bypass, this will write
+		 * a zero to TVE and disable the window;
+		 * opal_pci_map_pe_dma_window() does the same thing when
+		 * tce_table_size==0.
+		 */
+		rc = opal_pci_map_pe_dma_window_real(phb->opal_id,
+				pe->pe_number, window_id, 0, 0);
+		if (rc)
+			pe_err(pe, "OPAL error %lld configuring bypass window\n",
+				rc);
 
-		top = roundup_pow_of_two(top);
-		rc = opal_pci_map_pe_dma_window_real(pe->phb->opal_id,
-				pe->pe_number, window_id,
-				pe->table_group.tce64_start, top);
-	} else {
-		rc = opal_pci_map_pe_dma_window_real(pe->phb->opal_id,
-				pe->pe_number, window_id,
-				pe->table_group.tce64_start, 0);
+		pe->tce_bypass_enabled = false;
+		return;
+	}
+
+	if (pnv_iommu_bypass_mode == PNV_IOMMU_TCE_BYPASS) {
+		if (!opal_phb_set_option(phb->opal_id,
+					OPAL_PHB_OPTION_TVE1_4GB, 1)) {
+			pe->table_group.tce64_start = SZ_4G;
+			if (!pnv_pci_ioda2_set_bypass_iommu(pe,
+						pe->table_group.tce64_start)) {
+				pe->tce_bypass_enabled = true;
+				pe_info(pe, "Enabled 64-bit IOMMU bypass at %llx\n",
+						pe->table_group.tce64_start);
+				return;
+			}
+			pe_err(pe, "Enabled TVE1_4GB but failed to configure TCE table");
+		} else {
+			/* IOMMU bypass failed, fallback to direct bypass */
+			pnv_iommu_bypass_mode = PNV_IOMMU_NO_TRANSLATE;
+		}
+	}
+
+	if (pnv_iommu_bypass_mode == PNV_IOMMU_NO_TRANSLATE) {
+		/*
+		 * FIXME: if we enable dynamic switch, here we need to disable
+		 * OPAL_PCI_PHB_FLAG_TVE1_4GB
+		 */
+		top = roundup_pow_of_two(memblock_end_of_DRAM());
+		if (!opal_pci_map_pe_dma_window_real(phb->opal_id,
+					pe->pe_number, window_id,
+					pe->table_group.tce64_start, top)) {
+			pe->tce_bypass_enabled = true;
+			pe_info(pe, "Enabled 64-bit direct bypass at %llx\n",
+					pe->table_group.tce64_start);
+			return;
+		}
 	}
-	if (rc)
-		pe_err(pe, "OPAL error %lld configuring bypass window\n", rc);
-	else
-		pe->tce_bypass_enabled = enable;
 }
 
 static long pnv_pci_ioda2_create_table(int nid, int num, __u64 bus_offset,
@@ -2409,6 +2521,9 @@ static long pnv_pci_ioda2_setup_default_config(struct pnv_ioda_pe *pe)
 	const unsigned int tceshift = PAGE_SHIFT;
 	unsigned long res_start, res_end, tces_order, tcelevel_order, levels;
 
+	if (pnv_iommu_bypass_mode != PNV_IOMMU_BYPASS_DISABLED)
+		pnv_pci_ioda2_set_bypass(pe, true);
+
 	/*
 	 * crashkernel= specifies the kdump kernel's maximum memory at
 	 * some offset and there is no guaranteed the result is a power
@@ -2432,6 +2547,15 @@ static long pnv_pci_ioda2_setup_default_config(struct pnv_ioda_pe *pe)
 	 */
 	/* iommu_table::it_map uses 1 bit per IOMMU page, hence 8 */
 	window_size = min((maxblock * 8) << tceshift, max_memory);
+
+	/*
+	 * If we get TVE#1_4GB on, there is no point in having a huge default
+	 * DMA window.
+	 */
+	if (pnv_iommu_bypass_mode == PNV_IOMMU_TCE_BYPASS)
+		window_size = min_t(u64, pe->table_group.tce32_size,
+				window_size);
+
 	/* Each TCE level cannot exceed maxblock so go multilevel if needed */
 	tces_order = ilog2(window_size >> tceshift);
 	tcelevel_order = ilog2(maxblock >> 3);
@@ -2470,9 +2594,6 @@ static long pnv_pci_ioda2_setup_default_config(struct pnv_ioda_pe *pe)
 		return rc;
 	}
 
-	if (!pnv_iommu_bypass_disabled)
-		pnv_pci_ioda2_set_bypass(pe, true);
-
 	/*
 	 * Set table base for the case of IOMMU DMA use. Usually this is done
 	 * from dma_dev_setup() which is not called when a device is returned
@@ -2550,15 +2671,27 @@ static long pnv_pci_ioda2_create_table_userspace(
 {
 	struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe,
 			table_group);
-	__u64 bus_offset = num ?
-		pe->table_group.tce64_start : table_group->tce32_start;
-	long ret = pnv_pci_ioda2_create_table(pe->phb->hose->node,
-			num, bus_offset, page_shift, window_size, levels, true,
-			ptbl);
+	__u64 bus_offset = num ? table_group->tce64_start :
+				 table_group->tce32_start;
+	long ret;
 
-	if (!ret)
+	if (bus_offset == SZ_4G)
+		ret = pnv_pci_ioda2_create_table(pe->phb->hose->node,
+				num, 0, page_shift,
+				roundup_pow_of_two(window_size + bus_offset),
+				levels, true, ptbl);
+	else
+		ret = pnv_pci_ioda2_create_table(pe->phb->hose->node,
+				num, bus_offset, page_shift,
+				window_size,
+				levels, true, ptbl);
+
+	if (!ret) {
 		(*ptbl)->it_allocated_size = pnv_pci_ioda2_get_table_size(
 				page_shift, window_size, levels);
+		(*ptbl)->it_dmaoff = bus_offset >> page_shift;
+	}
+
 	return ret;
 }
 
@@ -2624,8 +2757,6 @@ static void pnv_ioda_setup_bus_iommu_group(struct pnv_ioda_pe *pe,
 				bus);
 }
 
-static unsigned long pnv_ioda_parse_tce_sizes(struct pnv_phb *phb);
-
 static void pnv_pci_ioda_setup_iommu_api(void)
 {
 	struct pci_controller *hose;
-- 
2.17.1


^ permalink raw reply related

* [PATCH kernel 5/5] vfio/spapr_tce: Advertise and allow a huge DMA windows at 4GB
From: Alexey Kardashevskiy @ 2020-02-18  7:36 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: Alexey Kardashevskiy, Alistair Popple, Alex Williamson, kvm-ppc,
	David Gibson
In-Reply-To: <20200218073650.16149-1-aik@ozlabs.ru>

So far the only option for a big 64big DMA window was a window located
at 0x800.0000.0000.0000 (1<<59) which creates problems for devices
supporting smaller DMA masks.

This exploits a POWER9 PHB option to allow the second DMA window to map
at 0 and advertises it with a 4GB offset to avoid overlap with
the default 32bit window.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
 include/uapi/linux/vfio.h           |  2 ++
 drivers/vfio/vfio_iommu_spapr_tce.c | 10 ++++++++--
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 9e843a147ead..c7f89d47335a 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -831,9 +831,11 @@ struct vfio_iommu_spapr_tce_info {
 	__u32 argsz;
 	__u32 flags;
 #define VFIO_IOMMU_SPAPR_INFO_DDW	(1 << 0)	/* DDW supported */
+#define VFIO_IOMMU_SPAPR_INFO_DDW_START	(1 << 1)	/* DDW offset */
 	__u32 dma32_window_start;	/* 32 bit window start (bytes) */
 	__u32 dma32_window_size;	/* 32 bit window size (bytes) */
 	struct vfio_iommu_spapr_tce_ddw_info ddw;
+	__u64 dma64_window_start;
 };
 
 #define VFIO_IOMMU_SPAPR_TCE_GET_INFO	_IO(VFIO_TYPE, VFIO_BASE + 12)
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 16b3adc508db..4f22be3c4aa2 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -691,7 +691,7 @@ static long tce_iommu_create_window(struct tce_container *container,
 	container->tables[num] = tbl;
 
 	/* Return start address assigned by platform in create_table() */
-	*start_addr = tbl->it_offset << tbl->it_page_shift;
+	*start_addr = tbl->it_dmaoff << tbl->it_page_shift;
 
 	return 0;
 
@@ -842,7 +842,13 @@ static long tce_iommu_ioctl(void *iommu_data,
 			info.ddw.levels = table_group->max_levels;
 		}
 
-		ddwsz = offsetofend(struct vfio_iommu_spapr_tce_info, ddw);
+		ddwsz = offsetofend(struct vfio_iommu_spapr_tce_info,
+				dma64_window_start);
+
+		if (info.argsz >= ddwsz) {
+			info.flags |= VFIO_IOMMU_SPAPR_INFO_DDW_START;
+			info.dma64_window_start = table_group->tce64_start;
+		}
 
 		if (info.argsz >= ddwsz)
 			minsz = ddwsz;
-- 
2.17.1


^ permalink raw reply related

* [PATCH v2 00/24] Manually convert  thermal, crypto and misc devices to ReST
From: Mauro Carvalho Chehab @ 2020-02-17 16:20 UTC (permalink / raw)
  To: Linux Doc Mailing List
  Cc: Rich Felker, linux-sh, Catalin Marinas, linux-pci, David Howells,
	keyrings, Paul Mackerras, Javi Merino, Will Deacon, Herbert Xu,
	Yoshinori Sato, Jonathan Corbet, Mauro Carvalho Chehab,
	Daniel Lezcano, Viresh Kumar, linux-pm, Bjorn Helgaas,
	Dan Williams, linux-arm-kernel, Amit Daniel Kachhap,
	Rafael J. Wysocki, linux-crypto, linuxppc-dev, David S. Miller


Manually convert some files from thermal, crypto and misc-devices
to ReST format.

This patch is against linux-next 20200217 tag.

v2: 

- a small change at patch 2 to avoid uneeded whitespace changes;
- added 13 new patches at the end

Mauro Carvalho Chehab (24):
  docs: thermal: convert cpu-idle-cooling.rst to ReST
  docs: crypto: convert asymmetric-keys.txt to ReST
  docs: crypto: convert api-intro.txt to ReST format
  docs: crypto: convert async-tx-api.txt to ReST format
  docs: crypto: descore-readme.txt: convert to ReST format
  docs: misc-devices/spear-pcie-gadget.txt: convert to ReST
  docs: misc-devices/pci-endpoint-test.txt: convert to ReST
  docs: misc-devices/pci-endpoint-test.txt: convert to ReST
  docs: misc-devices/c2port.txt: convert to ReST format
  docs: misc-devices/bh1770glc.txt: convert to ReST
  docs: misc-devices/apds990x.txt: convert to ReST format
  docs: pci: endpoint/function/binding/pci-test.txt convert to ReST
  docs: arm64: convert perf.txt to ReST format
  docs: cpu-freq: convert index.txt to ReST
  docs: cpu-freq: convert amd-powernow.txt to ReST
  docs: cpu-freq: convert core.txt to ReST
  docs: cpu-freq: convert cpu-drivers.txt to ReST
  docs: cpu-freq: convert cpufreq-nforce2.txt to ReST
  docs: cpu-freq: convert cpufreq-stats.txt to ReST
  docs: cpu-freq: convert pcc-cpufreq.txt to ReST
  docs: powerpc: convert vcpudispatch_stats.txt to ReST
  docs: sh: convert new-machine.txt to ReST
  docs: sh: convert register-banks.txt to ReST
  docs: trace: ring-buffer-design.txt: convert to ReST format

 .../endpoint/function/binding/pci-test.rst    |  26 +
 .../endpoint/function/binding/pci-test.txt    |  19 -
 Documentation/PCI/endpoint/index.rst          |   2 +
 Documentation/arm64/index.rst                 |   1 +
 Documentation/arm64/{perf.txt => perf.rst}    |   7 +-
 .../{amd-powernow.txt => amd-powernow.rst}    |  12 +-
 Documentation/cpu-freq/{core.txt => core.rst} |  65 +-
 .../{cpu-drivers.txt => cpu-drivers.rst}      | 129 ++-
 ...pufreq-nforce2.txt => cpufreq-nforce2.rst} |  18 +-
 .../{cpufreq-stats.txt => cpufreq-stats.rst}  | 121 +--
 Documentation/cpu-freq/index.rst              |  42 +
 Documentation/cpu-freq/index.txt              |  56 --
 .../{pcc-cpufreq.txt => pcc-cpufreq.rst}      |  86 +-
 .../crypto/{api-intro.txt => api-intro.rst}   | 186 ++--
 ...symmetric-keys.txt => asymmetric-keys.rst} |  91 +-
 .../{async-tx-api.txt => async-tx-api.rst}    | 253 +++---
 ...{descore-readme.txt => descore-readme.rst} | 152 +++-
 Documentation/crypto/index.rst                |   5 +
 .../driver-api/thermal/cpu-idle-cooling.rst   |  18 +-
 Documentation/driver-api/thermal/index.rst    |   1 +
 Documentation/index.rst                       |   1 +
 .../{ad525x_dpot.txt => ad525x_dpot.rst}      |  24 +-
 .../{apds990x.txt => apds990x.rst}            |  31 +-
 .../{bh1770glc.txt => bh1770glc.rst}          |  45 +-
 .../misc-devices/{c2port.txt => c2port.rst}   |  58 +-
 Documentation/misc-devices/index.rst          |   6 +
 .../misc-devices/pci-endpoint-test.rst        |  56 ++
 .../misc-devices/pci-endpoint-test.txt        |  41 -
 .../misc-devices/spear-pcie-gadget.rst        | 170 ++++
 .../misc-devices/spear-pcie-gadget.txt        | 130 ---
 Documentation/powerpc/index.rst               |   1 +
 ...patch_stats.txt => vcpudispatch_stats.rst} |  17 +-
 Documentation/sh/index.rst                    |   6 +
 .../sh/{new-machine.txt => new-machine.rst}   | 193 +++--
 ...{register-banks.txt => register-banks.rst} |  12 +-
 Documentation/trace/index.rst                 |   1 +
 ...ffer-design.txt => ring-buffer-design.rst} | 802 ++++++++++--------
 37 files changed, 1603 insertions(+), 1281 deletions(-)
 create mode 100644 Documentation/PCI/endpoint/function/binding/pci-test.rst
 delete mode 100644 Documentation/PCI/endpoint/function/binding/pci-test.txt
 rename Documentation/arm64/{perf.txt => perf.rst} (95%)
 rename Documentation/cpu-freq/{amd-powernow.txt => amd-powernow.rst} (89%)
 rename Documentation/cpu-freq/{core.txt => core.rst} (69%)
 rename Documentation/cpu-freq/{cpu-drivers.txt => cpu-drivers.rst} (72%)
 rename Documentation/cpu-freq/{cpufreq-nforce2.txt => cpufreq-nforce2.rst} (55%)
 rename Documentation/cpu-freq/{cpufreq-stats.txt => cpufreq-stats.rst} (53%)
 create mode 100644 Documentation/cpu-freq/index.rst
 delete mode 100644 Documentation/cpu-freq/index.txt
 rename Documentation/cpu-freq/{pcc-cpufreq.txt => pcc-cpufreq.rst} (80%)
 rename Documentation/crypto/{api-intro.txt => api-intro.rst} (70%)
 rename Documentation/crypto/{asymmetric-keys.txt => asymmetric-keys.rst} (91%)
 rename Documentation/crypto/{async-tx-api.txt => async-tx-api.rst} (55%)
 rename Documentation/crypto/{descore-readme.txt => descore-readme.rst} (81%)
 rename Documentation/misc-devices/{ad525x_dpot.txt => ad525x_dpot.rst} (85%)
 rename Documentation/misc-devices/{apds990x.txt => apds990x.rst} (86%)
 rename Documentation/misc-devices/{bh1770glc.txt => bh1770glc.rst} (83%)
 rename Documentation/misc-devices/{c2port.txt => c2port.rst} (59%)
 create mode 100644 Documentation/misc-devices/pci-endpoint-test.rst
 delete mode 100644 Documentation/misc-devices/pci-endpoint-test.txt
 create mode 100644 Documentation/misc-devices/spear-pcie-gadget.rst
 delete mode 100644 Documentation/misc-devices/spear-pcie-gadget.txt
 rename Documentation/powerpc/{vcpudispatch_stats.txt => vcpudispatch_stats.rst} (94%)
 rename Documentation/sh/{new-machine.txt => new-machine.rst} (73%)
 rename Documentation/sh/{register-banks.txt => register-banks.rst} (88%)
 rename Documentation/trace/{ring-buffer-design.txt => ring-buffer-design.rst} (55%)

-- 
2.24.1



^ permalink raw reply

* Re: [PATCH 2/2] powerpc/kernel/sysfs: Add new config option PMU_SYSFS to enable PMU SPRs sysfs file creation
From: Nageswara R Sastry @ 2020-02-18  8:52 UTC (permalink / raw)
  To: Kajol Jain; +Cc: anju, linuxppc-dev, maddy
In-Reply-To: <20200214080606.26872-2-kjain@linux.ibm.com>

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


"Kajol Jain" <kjain@linux.ibm.com> wrote on 14/02/2020 01:36:06 PM:

> From: "Kajol Jain" <kjain@linux.ibm.com>
> To: linuxppc-dev@lists.ozlabs.org, mpe@ellerman.id.au
> Cc: maddy@linux.vnet.ibm.com, anju@linux.vnet.ibm.com, Nageswara R
> Sastry/India/IBM@IBMIN, kjain@linux.ibm.com
> Date: 14/02/2020 01:36 PM
> Subject: [PATCH 2/2] powerpc/kernel/sysfs: Add new config option
> PMU_SYSFS to enable PMU SPRs sysfs file creation
>
> Many of the performance monitoring unit (PMU) SPRs are
> exposed in the sysfs. This may not be a desirable since
> "perf" API is the primary interface to program PMU and
> collect counter data in the system. But that said, we
> cant remove these sysfs files since we dont whether
> anyone/anything is using them.
>
> So the patch adds a new CONFIG option 'CONFIG_PMU_SYSFS'
> (user selectable) to be used in sysfs file creation for
> PMU SPRs. New option by default is disabled, but can be
> enabled if user needs it.
>
> Tested this patch behaviour in powernv and pseries machines.
> Patch is also tested for pmac32_defconfig.
>
> Signed-off-by: Kajol Jain <kjain@linux.ibm.com>

Tested-by: Nageswara R Sastry <nasastry@in.ibm.com>

Tested on PowerVM, PowerNV machines with and with out CONFIG_PMU_SYSFS
With CONFIG_PMU_SYSFS=y
    For online CPUs can see the SPR files namely mmcr*, pmc*
With CONFIG_PMU_SYSFS=n
    Not seeing the SPR files


> ---
>  arch/powerpc/kernel/sysfs.c            | 6 ++++++
>  arch/powerpc/platforms/Kconfig.cputype | 6 ++++++
>  2 files changed, 12 insertions(+)
>
> diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
> index 74da5eb..cd807e8 100644
> --- a/arch/powerpc/kernel/sysfs.c
> +++ b/arch/powerpc/kernel/sysfs.c
> @@ -562,6 +562,7 @@ void ppc_enable_pmcs(void)
>   * that are implemented on the current processor
>   */
>
> +#ifdef CONFIG_PMU_SYSFS
>  #if defined(CONFIG_PPC64) || defined(CONFIG_PPC_BOOK3S_32)
>  #define HAS_PPC_PMC_CLASSIC   1
>  #define HAS_PPC_PMC_IBM      1
> @@ -575,6 +576,7 @@ void ppc_enable_pmcs(void)
>  #ifdef CONFIG_PPC_BOOK3S_32
>  #define HAS_PPC_PMC_G4      1
>  #endif
> +#endif /* CONFIG_PMU_SYSFS */
>
>  #if defined(CONFIG_PPC64) && defined(CONFIG_DEBUG_MISC)
>  #define HAS_PPC_PA6T
> @@ -812,8 +814,10 @@ static int register_cpu_online(unsigned int cpu)
>           device_create_file(s, &pmc_attrs[i]);
>
>  #ifdef CONFIG_PPC64
> +#ifdef   CONFIG_PMU_SYSFS
>     if (cpu_has_feature(CPU_FTR_MMCRA))
>        device_create_file(s, &dev_attr_mmcra);
> +#endif /* CONFIG_PMU_SYSFS */
>
>     if (cpu_has_feature(CPU_FTR_PURR)) {
>        if (!firmware_has_feature(FW_FEATURE_LPAR))
> @@ -901,8 +905,10 @@ static int unregister_cpu_online(unsigned int cpu)
>           device_remove_file(s, &pmc_attrs[i]);
>
>  #ifdef CONFIG_PPC64
> +#ifdef CONFIG_PMU_SYSFS
>     if (cpu_has_feature(CPU_FTR_MMCRA))
>        device_remove_file(s, &dev_attr_mmcra);
> +#endif /* CONFIG_PMU_SYSFS */
>
>     if (cpu_has_feature(CPU_FTR_PURR))
>        device_remove_file(s, &dev_attr_purr);
> diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/
> platforms/Kconfig.cputype
> index 8d7f9c3..e58b48d 100644
> --- a/arch/powerpc/platforms/Kconfig.cputype
> +++ b/arch/powerpc/platforms/Kconfig.cputype
> @@ -417,6 +417,12 @@ config PPC_MM_SLICES
>  config PPC_HAVE_PMU_SUPPORT
>     bool
>
> +config PMU_SYSFS
> +   bool "Create PMU SPRs sysfs file"
> +   default n
> +   help
> +     This option enables sysfs file creation for PMU SPRs like
> MMCR* and PMC*.
> +
>  config PPC_PERF_CTRS
>     def_bool y
>     depends on PERF_EVENTS && PPC_HAVE_PMU_SUPPORT
> --
> 1.8.3.1
>

[-- Attachment #2: Type: text/html, Size: 5071 bytes --]

^ permalink raw reply

* Re: [PATCH] powerpc/kprobes: Fix trap address when trap happened in real mode
From: Masami Hiramatsu @ 2020-02-18 10:29 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: linux-kernel, Anil S Keshavamurthy, Paul Mackerras, stable,
	Naveen N. Rao, linuxppc-dev, David S. Miller, Larry Finger
In-Reply-To: <c93c5346-d964-9167-c4dd-3123917344cf@c-s.fr>

On Tue, 18 Feb 2020 06:58:06 +0100
Christophe Leroy <christophe.leroy@c-s.fr> wrote:

> >>>>
> >>>> What do you mean by 'there' ? At the entry of kprobe_handler() ?
> >>>>
> >>>> That's what my patch does, it checks whether MMU is disabled or not. If
> >>>> it is, it converts the address to a virtual address.
> >>>>
> >>>> Do you mean kprobe_handler() should bail out early as it does when the
> >>>> trap happens in user mode ?
> >>>
> >>> Yes, that is what I meant.
> >>>
> >>>> Of course we can do that, I don't know
> >>>> enough about kprobe to know if kprobe_handler() should manage events
> >>>> that happened in real-mode or just ignore them. But I tested adding an
> >>>> event on a function that runs in real-mode, and it (now) works.
> >>>>
> >>>> So, what should we do really ?
> >>>
> >>> I'm not sure how the powerpc kernel runs in real mode.
> >>> But clearly, at least kprobe event can not handle that case because
> >>> it tries to access memory by probe_kernel_read(). Unless that function
> >>> correctly handles the address translation, I want to prohibit kprobes
> >>> on such address.
> >>>
> >>> So what I would like to see is, something like below.
> >>>
> >>> diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c
> >>> index 2d27ec4feee4..4771be152416 100644
> >>> --- a/arch/powerpc/kernel/kprobes.c
> >>> +++ b/arch/powerpc/kernel/kprobes.c
> >>> @@ -261,7 +261,7 @@ int kprobe_handler(struct pt_regs *regs)
> >>>           unsigned int *addr = (unsigned int *)regs->nip;
> >>>           struct kprobe_ctlblk *kcb;
> >>>    
> >>> -       if (user_mode(regs))
> >>> +       if (user_mode(regs) || !(regs->msr & MSR_IR))
> >>>                   return 0;
> >>>    
> >>>           /*
> >>>
> >>>
> >>
> >> With this instead change of my patch, I get an Oops everytime a kprobe
> >> event occurs in real-mode.
> >>
> >> This is because kprobe_handler() is now saying 'this trap doesn't belong
> >> to me' for a trap that has been installed by it.
> > 
> > Hmm, on powerpc, kprobes is allowed to probe on the code which runs
> > in the real mode? I think we should also prohibit it by blacklisting.
> > (It is easy to add blacklist by NOKPROBE_SYMBOL(func))
> 
> Yes, I see a lot of them tagged with _ASM_NOKPROBE_SYMBOL() on PPC64, 
> but none on PPC32. I suppose that's missing and have to be added. 

Ah, you are using PPC32. 

> Nevertheless, if one symbol has been forgotten in the blacklist, I think 
> it is a problem if it generate Oopses.

There is a long history also on x86 to make a blacklist. Anyway, how did
you get this error on PPC32? Somewhere would you like to probe and
it is a real mode function? Or, it happened unexpectedly?

> 
> > Or, some parts are possble to run under both real mode and kernel mode?
> 
> I don't think so, at least on PPC32

OK, that's a good news. Also, are there any independent section where such
real mode functions are stored? (I can see start_real_trampolines in
sections.h) If that kind of sections are defined, it is easy to make
a blacklist in arch_populate_kprobe_blacklist(). See arch/arm64/kernel/probes/kprobes.c.


> >> So the 'program check' exception handler doesn't find the owner of the
> >> trap hence generate an Oops.
> >>
> >> Even if we don't want kprobe() to proceed with the event entirely
> >> (allthough it works at least for simple events), I'd expect it to fail
> >> gracefully.
> > 
> > Agreed. I thought it was easy to identify real mode code. But if it is
> > hard, we should apply your first patch and also skip user handlers
> > if we are in the real mode (and increment missed count).
> 
> user handlers are already skipped.

Yes, if you don't put a kprobes on real mode code. However, if user
(accidentally) puts a probe on real mode code, it might call a
user handler?

> 
> What do you think about my latest proposal below ? If a trap is 
> encoutered in real mode, if checks if the matching virtual address 
> corresponds to a valid kprobe. If it is, it skips it. If not, it returns 
> 0 to tell "it's no me". You are also talking about incrementing the 
> missed count. Who do we do that ?

I rather like your first patch. If there is a kprobes, we can not skip
the instruction, because there is an instruction which must be executed.
(or single-skipped, but I'm not sure the emulator works correctly on
real mode)

Thank you,

> 
> 
> 
> @@ -264,6 +265,13 @@ int kprobe_handler(struct pt_regs *regs)
>       if (user_mode(regs))
>           return 0;
> 
> +    if (!(regs->msr & MSR_IR)) {
> +        if (!get_kprobe(phys_to_virt(regs->nip)))
> +            return 0;
> +        regs->nip += 4;
> +        return 1;
> +    }
> +
>       /*
>        * We don't want to be preempted for the entire
>        * duration of kprobe processing
> 
> 
> > 
> > BTW, can the emulater handle the real mode code correctly?
> 
> I don't know, how do I test that ?
> 
> Christophe


-- 
Masami Hiramatsu <mhiramat@kernel.org>

^ permalink raw reply

* Re:Re: [PATCH] powerpc/sysdev: fix compile errors
From: 王文虎 @ 2020-02-18 10:25 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: Kate Stewart, trivial, Andrew Donnellan, wangwenhu,
	Greg Kroah-Hartman, linux-kernel, Richard Fontana, Paul Mackerras,
	Thomas Gleixner, linuxppc-dev, lonehugo
In-Reply-To: <d3b340dc-54df-24f6-2831-50456beea7ab@c-s.fr>


From: Christophe Leroy <christophe.leroy@c-s.fr>
 Date: 2020-01-21 16:37:07
To:"王文虎" <wenhu.wang@vivo.com>,Andrew Donnellan <ajd@linux.ibm.com>
 cc: Kate Stewart <kstewart@linuxfoundation.org>,Richard Fontana <rfontana@redhat.com>,Greg Kroah-Hartman <gregkh@linuxfoundation.org>,linux-kernel@vger.kernel.org,wangwenhu <wenhu.pku@gmail.com>,Paul Mackerras <paulus@samba.org>,trivial@kernel.org,Thomas Gleixner <tglx@linutronix.de>,linuxppc-dev@lists.ozlabs.org,lonehugo@hotmail.com
Subject: Re: [PATCH] powerpc/sysdev: fix compile errors>
>
>Le 21/01/2020 à 07:59, 王文虎 a écrit :
>> 发件人:Andrew Donnellan <ajd@linux.ibm.com>
>> 发送日期:2020-01-21 14:13:07
>> 收件人:wangwenhu <wenhu.pku@gmail.com>,Benjamin Herrenschmidt <benh@kernel.crashing.org>,Paul Mackerras <paulus@samba.org>,Michael Ellerman <mpe@ellerman.id.au>,Kate Stewart <kstewart@linuxfoundation.org>,Greg Kroah-Hartman <gregkh@linuxfoundation.org>,Richard Fontana <rfontana@redhat.com>,Thomas Gleixner <tglx@linutronix.de>,linuxppc-dev@lists.ozlabs.org,linux-kernel@vger.kernel.org
>> 抄送人:trivial@kernel.org,lonehugo@hotmail.com,wenhu.wang@vivo.com
>> 主题:Re: [PATCH] powerpc/sysdev: fix compile errors>On 21/1/20 4:31 pm, wangwenhu wrote:
>>>> From: wangwenhu <wenhu.wang@vivo.com>
>>>>
>>>> Include arch/powerpc/include/asm/io.h into fsl_85xx_cache_sram.c to
>>>> fix the implicit declaration compile errors when building Cache-Sram.
>>>>
>>>> arch/powerpc/sysdev/fsl_85xx_cache_sram.c: In function ‘instantiate_cache_sram’:
>>>> arch/powerpc/sysdev/fsl_85xx_cache_sram.c:97:26: error: implicit declaration of function ‘ioremap_coherent’; did you mean ‘bitmap_complement’? [-Werror=implicit-function-declaration]
>>>>     cache_sram->base_virt = ioremap_coherent(cache_sram->base_phys,
>>>>                             ^~~~~~~~~~~~~~~~
>>>>                             bitmap_complement
>>>> arch/powerpc/sysdev/fsl_85xx_cache_sram.c:97:24: error: assignment makes pointer from integer without a cast [-Werror=int-conversion]
>>>>     cache_sram->base_virt = ioremap_coherent(cache_sram->base_phys,
>>>>                           ^
>>>> arch/powerpc/sysdev/fsl_85xx_cache_sram.c:123:2: error: implicit declaration of function ‘iounmap’; did you mean ‘roundup’? [-Werror=implicit-function-declaration]
>>>>     iounmap(cache_sram->base_virt);
>>>>     ^~~~~~~
>>>>     roundup
>>>> cc1: all warnings being treated as errors
>>>>
>>>> Signed-off-by: wangwenhu <wenhu.wang@vivo.com>
>>>
>>> How long has this code been broken for?
>> 
>> It's been broken almost 15 months since the commit below:
>> "commit aa91796ec46339f2ed53da311bd3ea77a3e4dfe1
>> Author: Christophe Leroy <christophe.leroy@c-s.fr>
>> Date:   Tue Oct 9 13:51:41 2018 +0000
>> 
>>      powerpc: don't use ioremap_prot() nor __ioremap() unless really needed."
>> 
>> And we are working on it now for further development.
>
>That's pretty surprising. That commit didn't change the iounmap(). It 
>only replaced ioremap_prot() by ioremap_coherent(). Both are defined in io.h
>
>Christophe
>

The compile error exists since the uploading of the driver
Details below:
1. "ioremap_flags" defined in "asm/io.h" was used primarily(Wed Oct 13 17:30:56 2010):
(6db92cc9d07d: powerpc/85xx: add cache-sram support);
2. "ioremap_prot" was used to replace "ioremap_flags"
(40f1ce7fb7e8: powerpc: Remove ioremap_flags);
3. "ioremap_coherent" was used to replace "ioremap_prot":
(aa91796ec463: powerpc: don't use ioremap_prot() nor __ioremap() unless really needed.)

So I will do the re-patch with a "Fixed" tag and "<linux/io.h>" include modification.
Which commit should be referenced to append to "Fixed" tag? (No.1 or No.3 ?)

Wenhu

>> 
>>>
>>>> ---
>>>>    arch/powerpc/sysdev/fsl_85xx_cache_sram.c | 1 +
>>>>    1 file changed, 1 insertion(+)
>>>>
>>>> diff --git a/arch/powerpc/sysdev/fsl_85xx_cache_sram.c b/arch/powerpc/sysdev/fsl_85xx_cache_sram.c
>>>> index f6c665dac725..29b6868eff7d 100644
>>>> --- a/arch/powerpc/sysdev/fsl_85xx_cache_sram.c
>>>> +++ b/arch/powerpc/sysdev/fsl_85xx_cache_sram.c
>>>> @@ -17,6 +17,7 @@
>>>>    #include <linux/of_platform.h>
>>>>    #include <asm/pgtable.h>
>>>>    #include <asm/fsl_85xx_cache_sram.h>
>>>> +#include <asm/io.h>
>>>>
>>>>    #include "fsl_85xx_cache_ctlr.h"
>>>>
>>>
>>> -- 
>>> Andrew Donnellan              OzLabs, ADL Canberra
>>> ajd@linux.ibm.com             IBM Australia Limited
>>>
>> 
>> Wenhu
>> 



^ permalink raw reply

* Re: [PATCH v2 07/13] powerpc: add support for folded p4d page tables
From: Mike Rapoport @ 2020-02-18 10:54 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: Rich Felker, linux-ia64, Geert Uytterhoeven, linux-sh, linux-mm,
	Paul Mackerras, linux-hexagon, Will Deacon, kvmarm, Jonas Bonn,
	linux-arch, Brian Cain, Marc Zyngier, Russell King, Ley Foon Tan,
	Mike Rapoport, Catalin Marinas, Julien Thierry, uclinux-h8-devel,
	Fenghua Yu, Arnd Bergmann, Suzuki K Poulose, kvm-ppc,
	Stefan Kristiansson, openrisc, Stafford Horne, Guan Xuetao,
	linux-arm-kernel, Tony Luck, Yoshinori Sato, linux-kernel,
	James Morse, nios2-dev, Andrew Morton, linuxppc-dev
In-Reply-To: <c79b363c-a111-389a-5752-51cf85fa8c44@c-s.fr>

On Sun, Feb 16, 2020 at 11:41:07AM +0100, Christophe Leroy wrote:
> 
> 
> Le 16/02/2020 à 09:18, Mike Rapoport a écrit :
> > From: Mike Rapoport <rppt@linux.ibm.com>
> > 
> > Implement primitives necessary for the 4th level folding, add walks of p4d
> > level where appropriate and replace 5level-fixup.h with pgtable-nop4d.h.
> 
> I don't think it is worth adding all this additionnals walks of p4d, this
> patch could be limited to changes like:
> 
> -		pud = pud_offset(pgd, gpa);
> +		pud = pud_offset(p4d_offset(pgd, gpa), gpa);
> 
> The additionnal walks should be added through another patch the day powerpc
> need them.

Ok, I'll update the patch to reduce walking the p4d.
 
> See below for more comments.
> 
> > 
> > Signed-off-by: Mike Rapoport <rppt@linux.ibm.com>
> > Tested-by: Christophe Leroy <christophe.leroy@c-s.fr> # 8xx and 83xx
> > ---

...

> > diff --git a/arch/powerpc/include/asm/book3s/64/pgtable.h b/arch/powerpc/include/asm/book3s/64/pgtable.h
> > index 201a69e6a355..ddddbafff0ab 100644
> > --- a/arch/powerpc/include/asm/book3s/64/pgtable.h
> > +++ b/arch/powerpc/include/asm/book3s/64/pgtable.h
> > @@ -2,7 +2,7 @@
> >   #ifndef _ASM_POWERPC_BOOK3S_64_PGTABLE_H_
> >   #define _ASM_POWERPC_BOOK3S_64_PGTABLE_H_
> > -#include <asm-generic/5level-fixup.h>
> > +#include <asm-generic/pgtable-nop4d.h>
> >   #ifndef __ASSEMBLY__
> >   #include <linux/mmdebug.h>
> > @@ -251,7 +251,7 @@ extern unsigned long __pmd_frag_size_shift;
> >   /* Bits to mask out from a PUD to get to the PMD page */
> >   #define PUD_MASKED_BITS		0xc0000000000000ffUL
> >   /* Bits to mask out from a PGD to get to the PUD page */
> > -#define PGD_MASKED_BITS		0xc0000000000000ffUL
> > +#define P4D_MASKED_BITS		0xc0000000000000ffUL
> >   /*
> >    * Used as an indicator for rcu callback functions
> > @@ -949,54 +949,60 @@ static inline bool pud_access_permitted(pud_t pud, bool write)
> >   	return pte_access_permitted(pud_pte(pud), write);
> >   }
> > -#define pgd_write(pgd)		pte_write(pgd_pte(pgd))
> > +#define __p4d_raw(x)	((p4d_t) { __pgd_raw(x) })
> > +static inline __be64 p4d_raw(p4d_t x)
> > +{
> > +	return pgd_raw(x.pgd);
> > +}
> > +
> 
> Shouldn't this be defined in asm/pgtable-be-types.h, just like other
> __pxx_raw() ?

Ideally yes, but this creates weird header file dependencies and untangling
them would generate way too much churn.
 
> > +#define p4d_write(p4d)		pte_write(p4d_pte(p4d))
> > -static inline void pgd_clear(pgd_t *pgdp)
> > +static inline void p4d_clear(p4d_t *p4dp)
> >   {
> > -	*pgdp = __pgd(0);
> > +	*p4dp = __p4d(0);
> >   }

...

> > @@ -573,9 +596,15 @@ int kvmppc_create_pte(struct kvm *kvm, pgd_t *pgtable, pte_t pte,
> >   	/* Traverse the guest's 2nd-level tree, allocate new levels needed */
> >   	pgd = pgtable + pgd_index(gpa);
> > -	pud = NULL;
> > +	p4d = NULL;
> >   	if (pgd_present(*pgd))
> > -		pud = pud_offset(pgd, gpa);
> > +		p4d = p4d_offset(pgd, gpa);
> > +	else
> > +		new_p4d = p4d_alloc_one(kvm->mm, gpa);
> > +
> > +	pud = NULL;
> > +	if (p4d_present(*p4d))
> > +		pud = pud_offset(p4d, gpa);
> 
> Is it worth adding all this new code ?
> 
> My understanding is that the series objective is to get rid of
> __ARCH_HAS_5LEVEL_HACK, to to add support for 5 levels to an architecture
> that not need it (at least for now).
> If we want to add support for 5 levels, it can be done later in another
> patch.
> 
> Here I think your change could be limited to:
> 
> -		pud = pud_offset(pgd, gpa);
> +		pud = pud_offset(p4d_offset(pgd, gpa), gpa);

This won't work. Without __ARCH_USE_5LEVEL_HACK defined pgd_present() is
hardwired to 1 and the actual check for the top level is performed with
p4d_present(). The 'else' clause that allocates p4d will never be taken and
it could be removed, but I prefer to keep it for consistency.
 
> >   	else
> >   		new_pud = pud_alloc_one(kvm->mm, gpa);
> > @@ -597,12 +626,18 @@ int kvmppc_create_pte(struct kvm *kvm, pgd_t *pgtable, pte_t pte,
> >   	/* Now traverse again under the lock and change the tree */
> >   	ret = -ENOMEM;
> >   	if (pgd_none(*pgd)) {
> > +		if (!new_p4d)
> > +			goto out_unlock;
> > +		pgd_populate(kvm->mm, pgd, new_p4d);
> > +		new_p4d = NULL;
> > +	}
> > +	if (p4d_none(*p4d)) {
> >   		if (!new_pud)
> >   			goto out_unlock;
> > -		pgd_populate(kvm->mm, pgd, new_pud);
> > +		p4d_populate(kvm->mm, p4d, new_pud);
> >   		new_pud = NULL;
> >   	}
> > -	pud = pud_offset(pgd, gpa);
> > +	pud = pud_offset(p4d, gpa);
> >   	if (pud_is_leaf(*pud)) {
> >   		unsigned long hgpa = gpa & PUD_MASK;
> > @@ -1220,6 +1255,7 @@ static ssize_t debugfs_radix_read(struct file *file, char __user *buf,
> >   	pgd_t *pgt;
> >   	struct kvm_nested_guest *nested;
> >   	pgd_t pgd, *pgdp;
> > +	p4d_t p4d, *p4dp;
> >   	pud_t pud, *pudp;
> >   	pmd_t pmd, *pmdp;
> >   	pte_t *ptep;
> > @@ -1298,7 +1334,14 @@ static ssize_t debugfs_radix_read(struct file *file, char __user *buf,
> >   			continue;
> >   		}
> > -		pudp = pud_offset(&pgd, gpa);
> > +		p4dp = p4d_offset(&pgd, gpa);
> > +		p4d = READ_ONCE(*p4dp);
> > +		if (!(p4d_val(p4d) & _PAGE_PRESENT)) {
> > +			gpa = (gpa & P4D_MASK) + P4D_SIZE;
> > +			continue;
> > +		}
> > +
> > +		pudp = pud_offset(&p4d, gpa);
> 
> Same, here you are forcing a useless read with READ_ONCE().
> 
> Your change could be limited to
> 
> -		pudp = pud_offset(&pgd, gpa);
> +		pudp = pud_offset(p4d_offset(&pgd, gpa), gpa);

Here again the actual check must be done against p4d rather than pgd. We
could skip READ_ONCE() for pgd, but since it is a debugfs method I don't
think it is more important than code consistency.
 
> This comment applies to many other places.

I'll make another pass to see where we can take the shortcut and use 

	pudp = pud_offset(p4d_offset(...))
 
> >   		pud = READ_ONCE(*pudp);
> >   		if (!(pud_val(pud) & _PAGE_PRESENT)) {
> >   			gpa = (gpa & PUD_MASK) + PUD_SIZE;
> > diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c
> > index 3345f039a876..7a59f6863cec 100644
> > --- a/arch/powerpc/lib/code-patching.c
> > +++ b/arch/powerpc/lib/code-patching.c
> > @@ -107,13 +107,18 @@ static inline int unmap_patch_area(unsigned long addr)
> >   	pte_t *ptep;
> >   	pmd_t *pmdp;
> >   	pud_t *pudp;
> > +	p4d_t *p4dp;
> >   	pgd_t *pgdp;
> >   	pgdp = pgd_offset_k(addr);
> >   	if (unlikely(!pgdp))
> >   		return -EINVAL;
> > -	pudp = pud_offset(pgdp, addr);
> > +	p4dp = p4d_offset(pgdp, addr);
> > +	if (unlikely(!p4dp))
> > +		return -EINVAL;
> > +
> > +	pudp = pud_offset(p4dp, addr);
> >   	if (unlikely(!pudp))
> >   		return -EINVAL;
> > diff --git a/arch/powerpc/mm/book3s32/mmu.c b/arch/powerpc/mm/book3s32/mmu.c
> > index 0a1c65a2c565..b2fc3e71165c 100644
> > --- a/arch/powerpc/mm/book3s32/mmu.c
> > +++ b/arch/powerpc/mm/book3s32/mmu.c
> > @@ -312,7 +312,7 @@ void hash_preload(struct mm_struct *mm, unsigned long ea)
> >   	if (!Hash)
> >   		return;
> > -	pmd = pmd_offset(pud_offset(pgd_offset(mm, ea), ea), ea);
> > +	pmd = pmd_offset(pud_offset(p4d_offset(pgd_offset(mm, ea), ea), ea), ea);
> 
> If we continue like this, in ten years this like is going to be many
> kilometers long.
> 
> I think the above would be worth a generic helper.

Agree. My plan was to first unify all the architectures and then start
introducing the generic helpers, like e.g. pmd_offset_mm().
 
> >   	if (!pmd_none(*pmd))
> >   		add_hash_page(mm->context.id, ea, pmd_val(*pmd));
> >   }
> > diff --git a/arch/powerpc/mm/book3s32/tlb.c b/arch/powerpc/mm/book3s32/tlb.c
> > index 2fcd321040ff..175bc33b41b7 100644
> > --- a/arch/powerpc/mm/book3s32/tlb.c
> > +++ b/arch/powerpc/mm/book3s32/tlb.c
> > @@ -87,7 +87,7 @@ static void flush_range(struct mm_struct *mm, unsigned long start,
> >   	if (start >= end)
> >   		return;
> >   	end = (end - 1) | ~PAGE_MASK;
> > -	pmd = pmd_offset(pud_offset(pgd_offset(mm, start), start), start);
> > +	pmd = pmd_offset(pud_offset(p4d_offset(pgd_offset(mm, start), start), start), start);
> >   	for (;;) {
> >   		pmd_end = ((start + PGDIR_SIZE) & PGDIR_MASK) - 1;
> >   		if (pmd_end > end)
> > @@ -145,7 +145,7 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
> >   		return;
> >   	}
> >   	mm = (vmaddr < TASK_SIZE)? vma->vm_mm: &init_mm;
> > -	pmd = pmd_offset(pud_offset(pgd_offset(mm, vmaddr), vmaddr), vmaddr);
> > +	pmd = pmd_offset(pud_offset(p4d_offset(pgd_offset(mm, vmaddr), vmaddr), vmaddr), vmaddr);
> >   	if (!pmd_none(*pmd))
> >   		flush_hash_pages(mm->context.id, vmaddr, pmd_val(*pmd), 1);
> >   }
> > diff --git a/arch/powerpc/mm/book3s64/hash_pgtable.c b/arch/powerpc/mm/book3s64/hash_pgtable.c
> > index 64733b9cb20a..9cd15937e88a 100644
> > --- a/arch/powerpc/mm/book3s64/hash_pgtable.c
> > +++ b/arch/powerpc/mm/book3s64/hash_pgtable.c
> > @@ -148,6 +148,7 @@ void hash__vmemmap_remove_mapping(unsigned long start,
> >   int hash__map_kernel_page(unsigned long ea, unsigned long pa, pgprot_t prot)
> >   {
> >   	pgd_t *pgdp;
> > +	p4d_t *p4dp;
> >   	pud_t *pudp;
> >   	pmd_t *pmdp;
> >   	pte_t *ptep;
> > @@ -155,7 +156,8 @@ int hash__map_kernel_page(unsigned long ea, unsigned long pa, pgprot_t prot)
> >   	BUILD_BUG_ON(TASK_SIZE_USER64 > H_PGTABLE_RANGE);
> >   	if (slab_is_available()) {
> >   		pgdp = pgd_offset_k(ea);
> > -		pudp = pud_alloc(&init_mm, pgdp, ea);
> > +		p4dp = p4d_offset(pgdp, ea);
> > +		pudp = pud_alloc(&init_mm, p4dp, ea);
> 
> Could be a single line, without a new var.
> 
> -		pudp = pud_alloc(&init_mm, pgdp, ea);
> +		pudp = pud_alloc(&init_mm, p4d_offset(pgdp, ea), ea);
> 
> 
> Same kind of comments as already done apply to the rest.
> 
> Christophe

-- 
Sincerely yours,
Mike.

^ permalink raw reply

* Re: [5.6.0-rc2-next-20200218/powerpc] Boot failure on POWER9
From: Kirill Tkhai @ 2020-02-18 11:01 UTC (permalink / raw)
  To: Sachin Sant, Linux-Next Mailing List; +Cc: Michal Hocko, linuxppc-dev
In-Reply-To: <cf6be5f5-4bbc-0d34-fb64-33fd37bc48d9@virtuozzo.com>

On 18.02.2020 13:50, Kirill Tkhai wrote:
> Hi, Sachin,
> 
> On 18.02.2020 13:45, Sachin Sant wrote:
>> Todays next fails to boot on a POWER9 PowerVM logical partition
>> with following trace:
>>
>> [    8.767660] random: systemd: uninitialized urandom read (16 bytes read)
>> [    8.768629] BUG: Kernel NULL pointer dereference on read at 0x000073b0
>> [    8.768635] Faulting instruction address: 0xc0000000003d55f4
>> [    8.768641] Oops: Kernel access of bad area, sig: 11 [#1]
>> [    8.768645] LE PAGE_SIZE=64K MMU=Hash SMP NR_CPUS=2048 NUMA pSeries
>> [    8.768650] Modules linked in:
>> [    8.768655] CPU: 19 PID: 1 Comm: systemd Not tainted 5.6.0-rc2-next-20200218-autotest #1
>> [    8.768660] NIP:  c0000000003d55f4 LR: c0000000003d5b94 CTR: 0000000000000000
>> [    8.768666] REGS: c0000008b37836d0 TRAP: 0300   Not tainted  (5.6.0-rc2-next-20200218-autotest)
>> [    8.768671] MSR:  8000000000009033 <SF,EE,ME,IR,DR,RI,LE>  CR: 24004844  XER: 00000000
>> [    8.768679] CFAR: c00000000000dec4 DAR: 00000000000073b0 DSISR: 40000000 IRQMASK: 1
>> [    8.768679] GPR00: c0000000003d5b94 c0000008b3783960 c00000000155d400 c0000008b301f500
>> [    8.768679] GPR04: 0000000000000dc0 0000000000000002 c0000000003443d8 c0000008bb398620
>> [    8.768679] GPR08: 00000008ba2f0000 0000000000000001 0000000000000000 0000000000000000
>> [    8.768679] GPR12: 0000000024004844 c00000001ec52a00 0000000000000000 0000000000000000
>> [    8.768679] GPR16: c0000008a1b20048 c000000001595898 c000000001750c18 0000000000000002
>> [    8.768679] GPR20: c000000001750c28 c000000001624470 0000000fffffffe0 5deadbeef0000122
>> [    8.768679] GPR24: 0000000000000001 0000000000000dc0 0000000000000002 c0000000003443d8
>> [    8.768679] GPR28: c0000008b301f500 c0000008bb398620 0000000000000000 c00c000002287180
>> [    8.768727] NIP [c0000000003d55f4] ___slab_alloc+0x1f4/0x760
>> [    8.768732] LR [c0000000003d5b94] __slab_alloc+0x34/0x60
>> [    8.768735] Call Trace:
>> [    8.768739] [c0000008b3783960] [c0000000003d5734] ___slab_alloc+0x334/0x760 (unreliable)
>> [    8.768745] [c0000008b3783a40] [c0000000003d5b94] __slab_alloc+0x34/0x60
>> [    8.768751] [c0000008b3783a70] [c0000000003d6fa0] __kmalloc_node+0x110/0x490
>> [    8.768757] [c0000008b3783af0] [c0000000003443d8] kvmalloc_node+0x58/0x110
>> [    8.768763] [c0000008b3783b30] [c0000000003fee38] mem_cgroup_css_online+0x108/0x270
>> [    8.768769] [c0000008b3783b90] [c000000000235aa8] online_css+0x48/0xd0
>> [    8.768775] [c0000008b3783bc0] [c00000000023eaec] cgroup_apply_control_enable+0x2ec/0x4d0
>> [    8.768781] [c0000008b3783ca0] [c000000000242318] cgroup_mkdir+0x228/0x5f0
>> [    8.768786] [c0000008b3783d10] [c00000000051e170] kernfs_iop_mkdir+0x90/0xf0
>> [    8.768792] [c0000008b3783d50] [c00000000043dc00] vfs_mkdir+0x110/0x230
>> [    8.768797] [c0000008b3783da0] [c000000000441c90] do_mkdirat+0xb0/0x1a0
>> [    8.768804] [c0000008b3783e20] [c00000000000b278] system_call+0x5c/0x68
>> [    8.768808] Instruction dump:
>> [    8.768811] 7c421378 e95f0000 714a0001 4082fff0 4bffff64 60000000 60000000 faa10088
>> [    8.768818] 3ea2000c 3ab57070 7b4a1f24 7d55502a <e94a73b0> 2faa0000 409e0394 3d02002a
>> [    8.768826] ---[ end trace 631af2cb73507891 ]---
>> [    8.770876]
>> [    9.770887] Kernel panic - not syncing: Fatal exception
>>
>> Bisect reveals the problem was introduced in next-20200217 by following commit 
>>
>> commit a75056fc1e7c 
>> mm/memcontrol.c: allocate shrinker_map on appropriate NUMA node
>>
>> I can boot the kernel successfully if the patch is reverted. 
> 
> 
> could you please test your boot with original patch from here:
> 
> https://patchwork.kernel.org/patch/11360007/

After you tried the above patch instead of the problem patch,
do one more test and apply the below on current linux-next.
Please, say which of the patches makes your kernel bootable again.

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 63bb6a2aab81..7b9b48dcbc60 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -334,7 +334,7 @@ static int memcg_expand_one_shrinker_map(struct mem_cgroup *memcg,
 		if (!old)
 			return 0;
 
-		new = kvmalloc_node(sizeof(*new) + size, GFP_KERNEL, nid);
+		new = kmalloc_node(sizeof(*new) + size, GFP_KERNEL, nid);
 		if (!new)
 			return -ENOMEM;
 
@@ -378,7 +378,7 @@ static int memcg_alloc_shrinker_maps(struct mem_cgroup *memcg)
 	mutex_lock(&memcg_shrinker_map_mutex);
 	size = memcg_shrinker_map_size;
 	for_each_node(nid) {
-		map = kvzalloc_node(sizeof(*map) + size, GFP_KERNEL, nid);
+		map = kzalloc_node(sizeof(*map) + size, GFP_KERNEL, nid);
 		if (!map) {
 			memcg_free_shrinker_maps(memcg);
 			ret = -ENOMEM;



^ permalink raw reply related

* Re: [PATCH] powerpc/kprobes: Fix trap address when trap happened in real mode
From: Christophe Leroy @ 2020-02-18 11:04 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: linux-kernel, Anil S Keshavamurthy, Paul Mackerras, stable,
	Naveen N. Rao, linuxppc-dev, David S. Miller, Larry Finger
In-Reply-To: <20200218192905.a3ed969e8565901c4f69fa22@kernel.org>



Le 18/02/2020 à 11:29, Masami Hiramatsu a écrit :
> On Tue, 18 Feb 2020 06:58:06 +0100
> Christophe Leroy <christophe.leroy@c-s.fr> wrote:
> 
>>>>>>
>>>>>> What do you mean by 'there' ? At the entry of kprobe_handler() ?
>>>>>>
>>>>>> That's what my patch does, it checks whether MMU is disabled or not. If
>>>>>> it is, it converts the address to a virtual address.
>>>>>>
>>>>>> Do you mean kprobe_handler() should bail out early as it does when the
>>>>>> trap happens in user mode ?
>>>>>
>>>>> Yes, that is what I meant.
>>>>>
>>>>>> Of course we can do that, I don't know
>>>>>> enough about kprobe to know if kprobe_handler() should manage events
>>>>>> that happened in real-mode or just ignore them. But I tested adding an
>>>>>> event on a function that runs in real-mode, and it (now) works.
>>>>>>
>>>>>> So, what should we do really ?
>>>>>
>>>>> I'm not sure how the powerpc kernel runs in real mode.
>>>>> But clearly, at least kprobe event can not handle that case because
>>>>> it tries to access memory by probe_kernel_read(). Unless that function
>>>>> correctly handles the address translation, I want to prohibit kprobes
>>>>> on such address.
>>>>>
>>>>> So what I would like to see is, something like below.
>>>>>
>>>>> diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c
>>>>> index 2d27ec4feee4..4771be152416 100644
>>>>> --- a/arch/powerpc/kernel/kprobes.c
>>>>> +++ b/arch/powerpc/kernel/kprobes.c
>>>>> @@ -261,7 +261,7 @@ int kprobe_handler(struct pt_regs *regs)
>>>>>            unsigned int *addr = (unsigned int *)regs->nip;
>>>>>            struct kprobe_ctlblk *kcb;
>>>>>     
>>>>> -       if (user_mode(regs))
>>>>> +       if (user_mode(regs) || !(regs->msr & MSR_IR))
>>>>>                    return 0;
>>>>>     
>>>>>            /*
>>>>>
>>>>>
>>>>
>>>> With this instead change of my patch, I get an Oops everytime a kprobe
>>>> event occurs in real-mode.
>>>>
>>>> This is because kprobe_handler() is now saying 'this trap doesn't belong
>>>> to me' for a trap that has been installed by it.
>>>
>>> Hmm, on powerpc, kprobes is allowed to probe on the code which runs
>>> in the real mode? I think we should also prohibit it by blacklisting.
>>> (It is easy to add blacklist by NOKPROBE_SYMBOL(func))
>>
>> Yes, I see a lot of them tagged with _ASM_NOKPROBE_SYMBOL() on PPC64,
>> but none on PPC32. I suppose that's missing and have to be added.
> 
> Ah, you are using PPC32.
> 
>> Nevertheless, if one symbol has been forgotten in the blacklist, I think
>> it is a problem if it generate Oopses.
> 
> There is a long history also on x86 to make a blacklist. Anyway, how did
> you get this error on PPC32? Somewhere would you like to probe and
> it is a real mode function? Or, it happened unexpectedly?

The first Oops I got was triggered by a WARN_ON() kind of trap in real 
mode. The trap exception handler called kprobe_handler() which tried to 
read the instruction at the trap address (which was a real-mode address) 
so it triggered a Bad Access Fault.

This was initially the purpose of my patch.

After discussion with you, I started looking at what would be the effect 
of setting a kprobe event in a function which runs in real mode.

> 
>>
>>> Or, some parts are possble to run under both real mode and kernel mode?
>>
>> I don't think so, at least on PPC32
> 
> OK, that's a good news. Also, are there any independent section where such
> real mode functions are stored? (I can see start_real_trampolines in
> sections.h) If that kind of sections are defined, it is easy to make
> a blacklist in arch_populate_kprobe_blacklist(). See arch/arm64/kernel/probes/kprobes.c.

Part of them are in .head.text, and this section is already blacklisted 
throught function arch_within_kprobe_blacklist()

But there are several other functions which are not there. For instance, 
many things within entry_32.S, and also things in hash_low.S
On PPC64 (ie in entry_64.S) they were explicitely blacklisted with 
_ASM_NOKPROBE_SYMBOL(). We have to do the same on PPC64

> 
> 
>>>> So the 'program check' exception handler doesn't find the owner of the
>>>> trap hence generate an Oops.
>>>>
>>>> Even if we don't want kprobe() to proceed with the event entirely
>>>> (allthough it works at least for simple events), I'd expect it to fail
>>>> gracefully.
>>>
>>> Agreed. I thought it was easy to identify real mode code. But if it is
>>> hard, we should apply your first patch and also skip user handlers
>>> if we are in the real mode (and increment missed count).
>>
>> user handlers are already skipped.
> 
> Yes, if you don't put a kprobes on real mode code. However, if user
> (accidentally) puts a probe on real mode code, it might call a
> user handler?

Are we talking about the same thing ?

Only kernel code can run in real mode, so the following code at the 
beginning of kprobe_handler() does the job ?

	if (user_mode(regs))
		return 0;


> 
>>
>> What do you think about my latest proposal below ? If a trap is
>> encoutered in real mode, if checks if the matching virtual address
>> corresponds to a valid kprobe. If it is, it skips it. If not, it returns
>> 0 to tell "it's no me". You are also talking about incrementing the
>> missed count. Who do we do that ?
> 
> I rather like your first patch. If there is a kprobes, we can not skip
> the instruction, because there is an instruction which must be executed.
> (or single-skipped, but I'm not sure the emulator works correctly on
> real mode)

Oops, yes of course.

Christophe

^ permalink raw reply

* [PATCH] powerpc/Makefile: Mark phony targets as PHONY
From: Michael Ellerman @ 2020-02-18 11:18 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: masahiroy, linux-kbuild

Some of our phony targets are not marked as such. This can lead to
confusing errors, eg:

  $ make clean
  $ touch install
  $ make install
  make: 'install' is up to date.
  $

Fix it by adding them to the PHONY variable which is marked phony in
the top-level Makefile. In arch/powerpc/boot/Makefile we do it
manually.

Suggested-by: Masahiro Yamada <masahiroy@kernel.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
 arch/powerpc/Makefile      | 6 ++++++
 arch/powerpc/boot/Makefile | 2 ++
 2 files changed, 8 insertions(+)

diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index f35730548e42..cbe5ca4f0ee5 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -298,6 +298,7 @@ $(BOOT_TARGETS2): vmlinux
 	$(Q)$(MAKE) $(build)=$(boot) $(patsubst %,$(boot)/%,$@)
 
 
+PHONY += bootwrapper_install
 bootwrapper_install:
 	$(Q)$(MAKE) $(build)=$(boot) $(patsubst %,$(boot)/%,$@)
 
@@ -403,9 +404,11 @@ define archhelp
   @echo '  (minus the .dts extension).'
 endef
 
+PHONY += install
 install:
 	$(Q)$(MAKE) $(build)=$(boot) install
 
+PHONY += vdso_install
 vdso_install:
 ifdef CONFIG_PPC64
 	$(Q)$(MAKE) $(build)=arch/$(ARCH)/kernel/vdso64 $@
@@ -425,6 +428,7 @@ archprepare: checkbin
 ifdef CONFIG_STACKPROTECTOR
 prepare: stack_protector_prepare
 
+PHONY += stack_protector_prepare
 stack_protector_prepare: prepare0
 ifdef CONFIG_PPC64
 	$(eval KBUILD_CFLAGS += -mstack-protector-guard-offset=$(shell awk '{if ($$2 == "PACA_CANARY") print $$3;}' include/generated/asm-offsets.h))
@@ -436,10 +440,12 @@ endif
 ifdef CONFIG_SMP
 prepare: task_cpu_prepare
 
+PHONY += task_cpu_prepare
 task_cpu_prepare: prepare0
 	$(eval KBUILD_CFLAGS += -D_TASK_CPU=$(shell awk '{if ($$2 == "TASK_CPU") print $$3;}' include/generated/asm-offsets.h))
 endif
 
+PHONY += checkbin
 # Check toolchain versions:
 # - gcc-4.6 is the minimum kernel-wide version so nothing required.
 checkbin:
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index 0556bf4fc9e9..58d4b4667206 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -445,6 +445,8 @@ install: $(CONFIGURE) $(addprefix $(obj)/, $(image-y))
 zInstall: $(CONFIGURE) $(addprefix $(obj)/, $(image-y))
 	sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" $^
 
+.PHONY: install zInstall
+
 # anything not in $(targets)
 clean-files += $(image-) $(initrd-) cuImage.* dtbImage.* treeImage.* \
 	zImage zImage.initrd zImage.chrp zImage.coff zImage.holly \
-- 
2.21.1


^ permalink raw reply related

* Re: [5.6.0-rc2-next-20200218/powerpc] Boot failure on POWER9
From: Kirill Tkhai @ 2020-02-18 10:50 UTC (permalink / raw)
  To: Sachin Sant, Linux-Next Mailing List; +Cc: Michal Hocko, linuxppc-dev
In-Reply-To: <3381CD91-AB3D-4773-BA04-E7A072A63968@linux.vnet.ibm.com>

Hi, Sachin,

On 18.02.2020 13:45, Sachin Sant wrote:
> Todays next fails to boot on a POWER9 PowerVM logical partition
> with following trace:
> 
> [    8.767660] random: systemd: uninitialized urandom read (16 bytes read)
> [    8.768629] BUG: Kernel NULL pointer dereference on read at 0x000073b0
> [    8.768635] Faulting instruction address: 0xc0000000003d55f4
> [    8.768641] Oops: Kernel access of bad area, sig: 11 [#1]
> [    8.768645] LE PAGE_SIZE=64K MMU=Hash SMP NR_CPUS=2048 NUMA pSeries
> [    8.768650] Modules linked in:
> [    8.768655] CPU: 19 PID: 1 Comm: systemd Not tainted 5.6.0-rc2-next-20200218-autotest #1
> [    8.768660] NIP:  c0000000003d55f4 LR: c0000000003d5b94 CTR: 0000000000000000
> [    8.768666] REGS: c0000008b37836d0 TRAP: 0300   Not tainted  (5.6.0-rc2-next-20200218-autotest)
> [    8.768671] MSR:  8000000000009033 <SF,EE,ME,IR,DR,RI,LE>  CR: 24004844  XER: 00000000
> [    8.768679] CFAR: c00000000000dec4 DAR: 00000000000073b0 DSISR: 40000000 IRQMASK: 1
> [    8.768679] GPR00: c0000000003d5b94 c0000008b3783960 c00000000155d400 c0000008b301f500
> [    8.768679] GPR04: 0000000000000dc0 0000000000000002 c0000000003443d8 c0000008bb398620
> [    8.768679] GPR08: 00000008ba2f0000 0000000000000001 0000000000000000 0000000000000000
> [    8.768679] GPR12: 0000000024004844 c00000001ec52a00 0000000000000000 0000000000000000
> [    8.768679] GPR16: c0000008a1b20048 c000000001595898 c000000001750c18 0000000000000002
> [    8.768679] GPR20: c000000001750c28 c000000001624470 0000000fffffffe0 5deadbeef0000122
> [    8.768679] GPR24: 0000000000000001 0000000000000dc0 0000000000000002 c0000000003443d8
> [    8.768679] GPR28: c0000008b301f500 c0000008bb398620 0000000000000000 c00c000002287180
> [    8.768727] NIP [c0000000003d55f4] ___slab_alloc+0x1f4/0x760
> [    8.768732] LR [c0000000003d5b94] __slab_alloc+0x34/0x60
> [    8.768735] Call Trace:
> [    8.768739] [c0000008b3783960] [c0000000003d5734] ___slab_alloc+0x334/0x760 (unreliable)
> [    8.768745] [c0000008b3783a40] [c0000000003d5b94] __slab_alloc+0x34/0x60
> [    8.768751] [c0000008b3783a70] [c0000000003d6fa0] __kmalloc_node+0x110/0x490
> [    8.768757] [c0000008b3783af0] [c0000000003443d8] kvmalloc_node+0x58/0x110
> [    8.768763] [c0000008b3783b30] [c0000000003fee38] mem_cgroup_css_online+0x108/0x270
> [    8.768769] [c0000008b3783b90] [c000000000235aa8] online_css+0x48/0xd0
> [    8.768775] [c0000008b3783bc0] [c00000000023eaec] cgroup_apply_control_enable+0x2ec/0x4d0
> [    8.768781] [c0000008b3783ca0] [c000000000242318] cgroup_mkdir+0x228/0x5f0
> [    8.768786] [c0000008b3783d10] [c00000000051e170] kernfs_iop_mkdir+0x90/0xf0
> [    8.768792] [c0000008b3783d50] [c00000000043dc00] vfs_mkdir+0x110/0x230
> [    8.768797] [c0000008b3783da0] [c000000000441c90] do_mkdirat+0xb0/0x1a0
> [    8.768804] [c0000008b3783e20] [c00000000000b278] system_call+0x5c/0x68
> [    8.768808] Instruction dump:
> [    8.768811] 7c421378 e95f0000 714a0001 4082fff0 4bffff64 60000000 60000000 faa10088
> [    8.768818] 3ea2000c 3ab57070 7b4a1f24 7d55502a <e94a73b0> 2faa0000 409e0394 3d02002a
> [    8.768826] ---[ end trace 631af2cb73507891 ]---
> [    8.770876]
> [    9.770887] Kernel panic - not syncing: Fatal exception
> 
> Bisect reveals the problem was introduced in next-20200217 by following commit 
> 
> commit a75056fc1e7c 
> mm/memcontrol.c: allocate shrinker_map on appropriate NUMA node
> 
> I can boot the kernel successfully if the patch is reverted. 


could you please test your boot with original patch from here:

https://patchwork.kernel.org/patch/11360007/

?

^ permalink raw reply

* [5.6.0-rc2-next-20200218/powerpc] Boot failure on POWER9
From: Sachin Sant @ 2020-02-18 10:45 UTC (permalink / raw)
  To: Linux-Next Mailing List, Kirill Tkhai; +Cc: linuxppc-dev

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

Todays next fails to boot on a POWER9 PowerVM logical partition
with following trace:

[    8.767660] random: systemd: uninitialized urandom read (16 bytes read)
[    8.768629] BUG: Kernel NULL pointer dereference on read at 0x000073b0
[    8.768635] Faulting instruction address: 0xc0000000003d55f4
[    8.768641] Oops: Kernel access of bad area, sig: 11 [#1]
[    8.768645] LE PAGE_SIZE=64K MMU=Hash SMP NR_CPUS=2048 NUMA pSeries
[    8.768650] Modules linked in:
[    8.768655] CPU: 19 PID: 1 Comm: systemd Not tainted 5.6.0-rc2-next-20200218-autotest #1
[    8.768660] NIP:  c0000000003d55f4 LR: c0000000003d5b94 CTR: 0000000000000000
[    8.768666] REGS: c0000008b37836d0 TRAP: 0300   Not tainted  (5.6.0-rc2-next-20200218-autotest)
[    8.768671] MSR:  8000000000009033 <SF,EE,ME,IR,DR,RI,LE>  CR: 24004844  XER: 00000000
[    8.768679] CFAR: c00000000000dec4 DAR: 00000000000073b0 DSISR: 40000000 IRQMASK: 1
[    8.768679] GPR00: c0000000003d5b94 c0000008b3783960 c00000000155d400 c0000008b301f500
[    8.768679] GPR04: 0000000000000dc0 0000000000000002 c0000000003443d8 c0000008bb398620
[    8.768679] GPR08: 00000008ba2f0000 0000000000000001 0000000000000000 0000000000000000
[    8.768679] GPR12: 0000000024004844 c00000001ec52a00 0000000000000000 0000000000000000
[    8.768679] GPR16: c0000008a1b20048 c000000001595898 c000000001750c18 0000000000000002
[    8.768679] GPR20: c000000001750c28 c000000001624470 0000000fffffffe0 5deadbeef0000122
[    8.768679] GPR24: 0000000000000001 0000000000000dc0 0000000000000002 c0000000003443d8
[    8.768679] GPR28: c0000008b301f500 c0000008bb398620 0000000000000000 c00c000002287180
[    8.768727] NIP [c0000000003d55f4] ___slab_alloc+0x1f4/0x760
[    8.768732] LR [c0000000003d5b94] __slab_alloc+0x34/0x60
[    8.768735] Call Trace:
[    8.768739] [c0000008b3783960] [c0000000003d5734] ___slab_alloc+0x334/0x760 (unreliable)
[    8.768745] [c0000008b3783a40] [c0000000003d5b94] __slab_alloc+0x34/0x60
[    8.768751] [c0000008b3783a70] [c0000000003d6fa0] __kmalloc_node+0x110/0x490
[    8.768757] [c0000008b3783af0] [c0000000003443d8] kvmalloc_node+0x58/0x110
[    8.768763] [c0000008b3783b30] [c0000000003fee38] mem_cgroup_css_online+0x108/0x270
[    8.768769] [c0000008b3783b90] [c000000000235aa8] online_css+0x48/0xd0
[    8.768775] [c0000008b3783bc0] [c00000000023eaec] cgroup_apply_control_enable+0x2ec/0x4d0
[    8.768781] [c0000008b3783ca0] [c000000000242318] cgroup_mkdir+0x228/0x5f0
[    8.768786] [c0000008b3783d10] [c00000000051e170] kernfs_iop_mkdir+0x90/0xf0
[    8.768792] [c0000008b3783d50] [c00000000043dc00] vfs_mkdir+0x110/0x230
[    8.768797] [c0000008b3783da0] [c000000000441c90] do_mkdirat+0xb0/0x1a0
[    8.768804] [c0000008b3783e20] [c00000000000b278] system_call+0x5c/0x68
[    8.768808] Instruction dump:
[    8.768811] 7c421378 e95f0000 714a0001 4082fff0 4bffff64 60000000 60000000 faa10088
[    8.768818] 3ea2000c 3ab57070 7b4a1f24 7d55502a <e94a73b0> 2faa0000 409e0394 3d02002a
[    8.768826] ---[ end trace 631af2cb73507891 ]---
[    8.770876]
[    9.770887] Kernel panic - not syncing: Fatal exception

Bisect reveals the problem was introduced in next-20200217 by following commit 

commit a75056fc1e7c 
mm/memcontrol.c: allocate shrinker_map on appropriate NUMA node

I can boot the kernel successfully if the patch is reverted. 

Boot log attached.

Thanks
-Sachin


[-- Attachment #2: next-20200218.log --]
[-- Type: application/octet-stream, Size: 19761 bytes --]

[pexpect]#kexec -e
[22603.697266] kexec_core: Starting new kernel
[22603.717296] kexec: waiting for cpu 2 (physical 2) to enter 1 state
[22603.717308] kexec: waiting for cpu 1 (physical 1) to enter 2 state
[22603.717363] kexec: waiting for cpu 2 (physical 2) to enter 2 state
[22603.717375] kexec: waiting for cpu 3 (physical 3) to enter 2 state
[22603.717384] kexec: waiting for cpu 4 (physical 4) to enter 2 state
[22603.717395] kexec: waiting for cpu 15 (physical 15) to enter 2 state
[22603.717404] kexec: waiting for cpu 31 (physical 31) to enter 2 state
[22603.880936] kexec: Starting switchover sequence.
I'm in purgatory
[    0.000000] hash-mmu: Page sizes from device-tree:
[    0.000000] hash-mmu: base_shift=12: shift=12, sllp=0x0000, avpnm=0x00000000, tlbiel=1, penc=0
[    0.000000] hash-mmu: base_shift=12: shift=16, sllp=0x0000, avpnm=0x00000000, tlbiel=1, penc=7
[    0.000000] hash-mmu: base_shift=12: shift=24, sllp=0x0000, avpnm=0x00000000, tlbiel=1, penc=56
[    0.000000] hash-mmu: base_shift=16: shift=16, sllp=0x0110, avpnm=0x00000000, tlbiel=1, penc=1
[    0.000000] hash-mmu: base_shift=16: shift=24, sllp=0x0110, avpnm=0x00000000, tlbiel=1, penc=8
[    0.000000] hash-mmu: base_shift=24: shift=24, sllp=0x0100, avpnm=0x00000001, tlbiel=0, penc=0
[    0.000000] hash-mmu: base_shift=34: shift=34, sllp=0x0120, avpnm=0x000007ff, tlbiel=0, penc=3
[    0.000000] Using 1TB segments
[    0.000000] hash-mmu: Initializing hash mmu with SLB
[    0.000000] Linux version 5.6.0-rc2-next-20200218-autotest (root@ltc-zzci-2.aus.stglabs.ibm.com) (gcc version 8.3.1 20190507 (Red Hat 8.3.1-4) (GCC)) #1 SMP Tue Feb 18 03:28:40 CST 2020
[    0.000000] Found initrd at 0xc000000003160000:0xc000000004bb41ea
[    0.000000] Using pSeries machine description
[    0.000000] printk: bootconsole [udbg0] enabled
[    0.000000] Partition configured for 32 cpus.
[    0.000000] CPU maps initialized for 8 threads per core
[    0.000000] -----------------------------------------------------
[    0.000000] phys_mem_size     = 0x8c0000000
[    0.000000] dcache_bsize      = 0x80
[    0.000000] icache_bsize      = 0x80
[    0.000000] cpu_features      = 0x0001c07f8f5f91a7
[    0.000000]   possible        = 0x0003fbffcf5fb1a7
[    0.000000]   always          = 0x00000003800081a1
[    0.000000] cpu_user_features = 0xdc0065c2 0xefe00000
[    0.000000] mmu_features      = 0x7c006001
[    0.000000] firmware_features = 0x00000097c45bfc57
[    0.000000] vmalloc start     = 0xc008000000000000
[    0.000000] IO start          = 0xc00a000000000000
[    0.000000] vmemmap start     = 0xc00c000000000000
[    0.000000] hash-mmu: ppc64_pft_size    = 0x1c
[    0.000000] hash-mmu: htab_hash_mask    = 0x1fffff
[    0.000000] -----------------------------------------------------
[    0.000000] numa:   NODE_DATA [mem 0x8bfedc900-0x8bfee3fff]
[    0.000000] numa:     NODE_DATA(0) on node 1
[    0.000000] numa:   NODE_DATA [mem 0x8bfed5200-0x8bfedc8ff]
[    0.000000] rfi-flush: fallback displacement flush available
[    0.000000] rfi-flush: mttrig type flush available
[    0.000000] link-stack-flush: software flush enabled.
[    0.000000] count-cache-flush: software flush disabled.
[    0.000000] stf-barrier: eieio barrier available
[    0.000000] lpar: H_BLOCK_REMOVE supports base psize:0 psize:0 block size:8
[    0.000000] lpar: H_BLOCK_REMOVE supports base psize:0 psize:2 block size:8
[    0.000000] lpar: H_BLOCK_REMOVE supports base psize:0 psize:10 block size:8
[    0.000000] lpar: H_BLOCK_REMOVE supports base psize:2 psize:2 block size:8
[    0.000000] lpar: H_BLOCK_REMOVE supports base psize:2 psize:10 block size:8
[    0.000000] PPC64 nvram contains 15360 bytes
[    0.000000] barrier-nospec: using ORI speculation barrier
[    0.000000] Zone ranges:
[    0.000000]   Normal   [mem 0x0000000000000000-0x00000008bfffffff]
[    0.000000]   Device   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   1: [mem 0x0000000000000000-0x00000008bfffffff]
[    0.000000] Could not find start_pfn for node 0
[    0.000000] Initmem setup node 0 [mem 0x0000000000000000-0x0000000000000000]
[    0.000000] Initmem setup node 1 [mem 0x0000000000000000-0x00000008bfffffff]
[    0.000000] percpu: Embedded 11 pages/cpu s624024 r0 d96872 u1048576
[    0.000000] Built 2 zonelists, mobility grouping on.  Total pages: 572880
[    0.000000] Policy zone: Normal
[    0.000000] Kernel command line: root=UUID=681ebf25-b7c8-49b9-b247-35a96bc8183f 
[    0.000000] Dentry cache hash table entries: 8388608 (order: 10, 67108864 bytes, linear)
[    0.000000] Inode-cache hash table entries: 4194304 (order: 9, 33554432 bytes, linear)
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 36388352K/36700160K available (11840K kernel code, 1728K rwdata, 3712K rodata, 4992K init, 2842K bss, 311808K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=128, Order=0-3, MinObjects=0, CPUs=32, Nodes=32
[    0.000000] ftrace: allocating 29812 entries in 11 pages
[    0.000000] ftrace: allocated 11 pages with 3 groups
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu: 	RCU restricting CPUs from NR_CPUS=2048 to nr_cpu_ids=32.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 10 jiffies.
[    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=32
[    0.000000] NR_IRQS: 512, nr_irqs: 512, preallocated irqs: 16
[    0.000000] xive: Using IRQ range [94000-9401f]
[    0.000000] xive: Interrupt handling initialized with spapr backend
[    0.000000] xive: Using priority 7 for all interrupts
[    0.000000] xive: Using 64kB queues
[    0.000000] rcu: 	Offload RCU callbacks from CPUs: (none).
[    0.000000] random: get_random_u64 called from start_kernel+0x704/0x960 with crng_init=0
[    0.000001] time_init: 56 bit decrementer (max: 7fffffffffffff)
[    0.000065] clocksource: timebase: mask: 0xffffffffffffffff max_cycles: 0x761537d007, max_idle_ns: 440795202126 ns
[    0.000172] clocksource: timebase mult[1f40000] shift[24] registered
[    0.000299] Console: colour dummy device 80x25
[    0.000348] printk: console [hvc0] enabled
[    0.000348] printk: console [hvc0] enabled
[    0.000395] printk: bootconsole [udbg0] disabled
[    0.000395] printk: bootconsole [udbg0] disabled
[    0.000472] mempolicy: Enabling automatic NUMA balancing. Configure with numa_balancing= or the kernel.numa_balancing sysctl
[    0.000485] pid_max: default: 32768 minimum: 301
[    0.000640] Mount-cache hash table entries: 131072 (order: 4, 1048576 bytes, linear)
[    0.000711] Mountpoint-cache hash table entries: 131072 (order: 4, 1048576 bytes, linear)
[    0.001521] EEH: pSeries platform initialized
[    0.001529] POWER9 performance monitor hardware support registered
[    0.001562] rcu: Hierarchical SRCU implementation.
[    0.002261] smp: Bringing up secondary CPUs ...
[    0.009907] smp: Brought up 2 nodes, 32 CPUs
[    0.009915] numa: Node 0 CPUs:
[    0.009918] numa: Node 1 CPUs: 0-31
[    0.009922] Using small cores at SMT level
[    0.009925] Using shared cache scheduler topology
[    0.010886] devtmpfs: initialized
[    0.013983] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.013993] futex hash table entries: 8192 (order: 4, 1048576 bytes, linear)
[    0.014219] thermal_sys: Registered thermal governor 'fair_share'
[    0.014220] thermal_sys: Registered thermal governor 'step_wise'
[    0.014326] NET: Registered protocol family 16
[    0.014548] audit: initializing netlink subsys (disabled)
[    0.014651] audit: type=2000 audit(1582021217.010:1): state=initialized audit_enabled=0 res=1
[    0.014745] cpuidle: using governor menu
[    0.014918] pstore: Registered nvram as persistent store backend
[    0.019457] PCI: Probing PCI hardware
[    0.019464] EEH: No capable adapters found: recovery disabled.
[    0.019528] pseries-rng: Registering arch random hook.
[    0.021204] HugeTLB registered 16.0 MiB page size, pre-allocated 0 pages
[    0.021211] HugeTLB registered 16.0 GiB page size, pre-allocated 0 pages
[    0.262509] random: fast init done
[    0.263568] iommu: Default domain type: Translated 
[    0.263610] vgaarb: loaded
[    0.263690] SCSI subsystem initialized
[    0.263721] usbcore: registered new interface driver usbfs
[    0.263730] usbcore: registered new interface driver hub
[    0.263809] usbcore: registered new device driver usb
[    0.263942] EDAC MC: Ver: 3.0.0
[    0.264240] clocksource: Switched to clocksource timebase
[    0.275109] VFS: Disk quotas dquot_6.6.0
[    0.275134] VFS: Dquot-cache hash table entries: 8192 (order 0, 65536 bytes)
[    0.277054] NET: Registered protocol family 2
[    0.277232] tcp_listen_portaddr_hash hash table entries: 32768 (order: 3, 524288 bytes, linear)
[    0.277285] TCP established hash table entries: 524288 (order: 6, 4194304 bytes, linear)
[    0.278078] TCP bind hash table entries: 65536 (order: 4, 1048576 bytes, linear)
[    0.278171] TCP: Hash tables configured (established 524288 bind 65536)
[    0.278207] UDP hash table entries: 32768 (order: 4, 1048576 bytes, linear)
[    0.278345] UDP-Lite hash table entries: 32768 (order: 4, 1048576 bytes, linear)
[    0.278790] NET: Registered protocol family 1
[    0.278799] PCI: CLS 0 bytes, default 128
[    0.278835] Trying to unpack rootfs image as initramfs...
[    0.688786] Freeing initrd memory: 26944K
[    0.691421] IOMMU table initialized, virtual merging enabled
[    0.711501] hv-24x7: read 1530 catalog entries, created 537 event attrs (0 failures), 275 descs
[    0.712517] workingset: timestamp_bits=38 max_order=20 bucket_order=0
[    0.713567] zbud: loaded
[    0.722396] NET: Registered protocol family 38
[    0.722402] Key type asymmetric registered
[    0.722406] Asymmetric key parser 'x509' registered
[    0.722416] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 249)
[    0.722510] io scheduler mq-deadline registered
[    0.722514] io scheduler kyber registered
[    0.722936] atomic64_test: passed
[    0.722969] PowerPC PowerNV PCI Hotplug Driver version: 0.1
[    0.723277] Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
[    0.723505] Non-volatile memory driver v1.3
[    0.723533] Linux agpgart interface v0.103
[    8.634413] tpm_ibmvtpm 30000003: ibmvtpm device is not ready
[    8.634418] tpm_ibmvtpm 30000003: CRQ initialization completed
[    8.634423] tpm_ibmvtpm 30000003: ibmvtpm device is not ready
[    8.634763] rdac: device handler registered
[    8.634822] hp_sw: device handler registered
[    8.634827] emc: device handler registered
[    8.634934] alua: device handler registered
[    8.635049] libphy: Fixed MDIO Bus: probed
[    8.635103] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    8.635119] ehci-pci: EHCI PCI platform driver
[    8.635131] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[    8.635145] ohci-pci: OHCI PCI platform driver
[    8.635157] uhci_hcd: USB Universal Host Controller Interface driver
[    8.635201] usbcore: registered new interface driver usbserial_generic
[    8.635211] usbserial: USB Serial support registered for generic
[    8.635292] mousedev: PS/2 mouse device common for all mice
[    8.635444] rtc-generic rtc-generic: registered as rtc0
[    8.635933] nx_compress_pseries ibm,compression-v1: nx842_OF_upd: max_sync_size new:65536 old:0
[    8.635944] nx_compress_pseries ibm,compression-v1: nx842_OF_upd: max_sync_sg new:510 old:0
[    8.635952] nx_compress_pseries ibm,compression-v1: nx842_OF_upd: max_sg_len new:4080 old:0
[    8.636029] alg: No test for 842 (842-nx)
[    8.637537] hid: raw HID events driver (C) Jiri Kosina
[    8.637671] usbcore: registered new interface driver usbhid
[    8.637677] usbhid: USB HID core driver
[    8.637748] drop_monitor: Initializing network drop monitor service
[    8.637857] Initializing XFRM netlink socket
[    8.638055] NET: Registered protocol family 10
[    8.638579] Segment Routing with IPv6
[    8.638620] NET: Registered protocol family 17
[    8.639674] registered taskstats version 1
[    8.639728] zswap: loaded using pool lzo/zbud
[    8.639899] pstore: Using crash dump compression: deflate
[    8.644602] Key type big_key registered
[    8.644763] rtc-generic rtc-generic: setting system clock to 2020-02-18T10:20:26 UTC (1582021226)
[    8.646127] Freeing unused kernel memory: 4992K
[    8.646132] Kernel memory protection not selected by kernel config.
[    8.646136] Run /init as init process
[    8.655323] systemd[1]: systemd 239 running in system mode. (+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN2 -IDN +PCRE2 default-hierarchy=legacy)
[    8.655511] systemd[1]: Detected architecture ppc64-le.
[    8.655518] systemd[1]: Running in initial RAM disk.

Welcome to Red Hat Enterprise Linux 8.1 Beta (Ootpa) dracut-049-26.git20190806.el8 (Initramfs)!

[    8.714833] systemd[1]: Set hostname to <ltc-zzci-2.aus.stglabs.ibm.com>.
[    8.767334] random: systemd: uninitialized urandom read (16 bytes read)
[    8.767443] systemd[1]: Listening on Journal Socket.
[  OK  ] Listening on Journal Socket.
[    8.767588] random: systemd: uninitialized urandom read (16 bytes read)
[    8.767600] systemd[1]: Reached target Local File Systems.
[  OK  ] Reached target Local File Systems.
[    8.767660] random: systemd: uninitialized urandom read (16 bytes read)
[    8.768629] BUG: Kernel NULL pointer dereference on read at 0x000073b0
[    8.768635] Faulting instruction address: 0xc0000000003d55f4
[    8.768641] Oops: Kernel access of bad area, sig: 11 [#1]
[    8.768645] LE PAGE_SIZE=64K MMU=Hash SMP NR_CPUS=2048 NUMA pSeries
[    8.768650] Modules linked in:
[    8.768655] CPU: 19 PID: 1 Comm: systemd Not tainted 5.6.0-rc2-next-20200218-autotest #1
[    8.768660] NIP:  c0000000003d55f4 LR: c0000000003d5b94 CTR: 0000000000000000
[    8.768666] REGS: c0000008b37836d0 TRAP: 0300   Not tainted  (5.6.0-rc2-next-20200218-autotest)
[    8.768671] MSR:  8000000000009033 <SF,EE,ME,IR,DR,RI,LE>  CR: 24004844  XER: 00000000
[    8.768679] CFAR: c00000000000dec4 DAR: 00000000000073b0 DSISR: 40000000 IRQMASK: 1 
[    8.768679] GPR00: c0000000003d5b94 c0000008b3783960 c00000000155d400 c0000008b301f500 
[    8.768679] GPR04: 0000000000000dc0 0000000000000002 c0000000003443d8 c0000008bb398620 
[    8.768679] GPR08: 00000008ba2f0000 0000000000000001 0000000000000000 0000000000000000 
[    8.768679] GPR12: 0000000024004844 c00000001ec52a00 0000000000000000 0000000000000000 
[    8.768679] GPR16: c0000008a1b20048 c000000001595898 c000000001750c18 0000000000000002 
[    8.768679] GPR20: c000000001750c28 c000000001624470 0000000fffffffe0 5deadbeef0000122 
[    8.768679] GPR24: 0000000000000001 0000000000000dc0 0000000000000002 c0000000003443d8 
[    8.768679] GPR28: c0000008b301f500 c0000008bb398620 0000000000000000 c00c000002287180 
[    8.768727] NIP [c0000000003d55f4] ___slab_alloc+0x1f4/0x760
[    8.768732] LR [c0000000003d5b94] __slab_alloc+0x34/0x60
[    8.768735] Call Trace:
[    8.768739] [c0000008b3783960] [c0000000003d5734] ___slab_alloc+0x334/0x760 (unreliable)
[    8.768745] [c0000008b3783a40] [c0000000003d5b94] __slab_alloc+0x34/0x60
[    8.768751] [c0000008b3783a70] [c0000000003d6fa0] __kmalloc_node+0x110/0x490
[    8.768757] [c0000008b3783af0] [c0000000003443d8] kvmalloc_node+0x58/0x110
[    8.768763] [c0000008b3783b30] [c0000000003fee38] mem_cgroup_css_online+0x108/0x270
[    8.768769] [c0000008b3783b90] [c000000000235aa8] online_css+0x48/0xd0
[    8.768775] [c0000008b3783bc0] [c00000000023eaec] cgroup_apply_control_enable+0x2ec/0x4d0
[    8.768781] [c0000008b3783ca0] [c000000000242318] cgroup_mkdir+0x228/0x5f0
[    8.768786] [c0000008b3783d10] [c00000000051e170] kernfs_iop_mkdir+0x90/0xf0
[    8.768792] [c0000008b3783d50] [c00000000043dc00] vfs_mkdir+0x110/0x230
[    8.768797] [c0000008b3783da0] [c000000000441c90] do_mkdirat+0xb0/0x1a0
[    8.768804] [c0000008b3783e20] [c00000000000b278] system_call+0x5c/0x68
[    8.768808] Instruction dump:
[    8.768811] 7c421378 e95f0000 714a0001 4082fff0 4bffff64 60000000 60000000 faa10088 
[    8.768818] 3ea2000c 3ab57070 7b4a1f24 7d55502a <e94a73b0> 2faa0000 409e0394 3d02002a 
[    8.768826] ---[ end trace 631af2cb73507891 ]---
[    8.770876] 
[    9.770887] Kernel panic - not syncing: Fatal exception
[    9.778279] ------------[ cut here ]------------
[    9.778289] WARNING: CPU: 19 PID: 1 at drivers/tty/vt/vt.c:4267 do_unblank_screen+0x190/0x250
[    9.778298] Modules linked in:
[    9.778304] CPU: 19 PID: 1 Comm: systemd Tainted: G      D           5.6.0-rc2-next-20200218-autotest #1
[    9.778313] NIP:  c0000000006e9180 LR: c0000000006e916c CTR: c000000000b74c60
[    9.778321] REGS: c0000008b37831b0 TRAP: 0700   Tainted: G      D            (5.6.0-rc2-next-20200218-autotest)
[    9.778330] MSR:  8000000000021033 <SF,ME,IR,DR,RI,LE>  CR: 28002242  XER: 2004000c
[    9.778341] CFAR: c0000000001c74b8 IRQMASK: 3 
[    9.778341] GPR00: c0000000006e916c c0000008b3783440 c00000000155d400 0000000000000000 
[    9.778341] GPR04: 0000000000000003 c0000008ad01400e 0000000000001dd7 c0000008b3783380 
[    9.778341] GPR08: c000000001423760 0000000000000000 0000000000000000 c0000008b37831ff 
[    9.778341] GPR12: 0000000028002448 c00000001ec52a00 0000000000000000 0000000000000000 
[    9.778341] GPR16: c0000008a1b20048 c000000001595898 c000000001750c18 0000000000000002 
[    9.778341] GPR20: c000000001750c28 c000000001624470 0000000fffffffe0 5deadbeef0000122 
[    9.778341] GPR24: 0000000000000001 0000000000000dc0 c00000000142c740 c0000000003443d8 
[    9.778341] GPR28: c000000001636f58 c000000001636f80 0000000000000000 c000000001745a08 
[    9.778410] NIP [c0000000006e9180] do_unblank_screen+0x190/0x250
[    9.778418] LR [c0000000006e916c] do_unblank_screen+0x17c/0x250
[    9.778424] Call Trace:
[    9.778429] [c0000008b3783440] [c0000000006e919c] do_unblank_screen+0x1ac/0x250 (unreliable)
[    9.778440] [c0000008b37834c0] [c00000000013e6b8] panic+0x1e8/0x410
[    9.778448] [c0000008b3783560] [c00000000002c6fc] oops_end+0x1ac/0x1b0
[    9.778457] [c0000008b37835e0] [c000000000086790] bad_page_fault+0x190/0x1e0
[    9.778466] [c0000008b3783660] [c00000000000a8a4] handle_page_fault+0x2c/0x30
[    9.778476] --- interrupt: 300 at ___slab_alloc+0x1f4/0x760
[    9.778476]     LR = __slab_alloc+0x34/0x60
[    9.778486] [c0000008b3783960] [c0000000003d5734] ___slab_alloc+0x334/0x760 (unreliable)
[    9.778495] [c0000008b3783a40] [c0000000003d5b94] __slab_alloc+0x34/0x60
[    9.778504] [c0000008b3783a70] [c0000000003d6fa0] __kmalloc_node+0x110/0x490
[    9.778513] [c0000008b3783af0] [c0000000003443d8] kvmalloc_node+0x58/0x110
[    9.778521] [c0000008b3783b30] [c0000000003fee38] mem_cgroup_css_online+0x108/0x270
[    9.778530] [c0000008b3783b90] [c000000000235aa8] online_css+0x48/0xd0
[    9.778538] [c0000008b3783bc0] [c00000000023eaec] cgroup_apply_control_enable+0x2ec/0x4d0
[    9.778547] [c0000008b3783ca0] [c000000000242318] cgroup_mkdir+0x228/0x5f0
[    9.778556] [c0000008b3783d10] [c00000000051e170] kernfs_iop_mkdir+0x90/0xf0
[    9.778565] [c0000008b3783d50] [c00000000043dc00] vfs_mkdir+0x110/0x230
[    9.778572] [c0000008b3783da0] [c000000000441c90] do_mkdirat+0xb0/0x1a0
[    9.778581] [c0000008b3783e20] [c00000000000b278] system_call+0x5c/0x68
[    9.778587] Instruction dump:
[    9.778592] 4e800020 60000000 60000000 60000000 7c0802a6 f8010090 4bade341 60000000 
[    9.778604] 813f0000 7d231b78 2f830000 409e0034 <0fe00000> e8010090 7c0803a6 4bfffeac 
[    9.778615] ---[ end trace 631af2cb73507892 ]---
[    9.778621] Rebooting in 10 seconds..


^ permalink raw reply


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