* [PATCH V6 0/5] ECAM quirks handling for ARM64 platforms
From: Bjorn Helgaas @ 2016-09-21 17:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <25529f96-3e80-0524-82d8-6eeb517df5b5@codeaurora.org>
On Wed, Sep 21, 2016 at 10:07:36AM -0400, Sinan Kaya wrote:
> On 9/21/2016 9:11 AM, Bjorn Helgaas wrote:
> > On Tue, Sep 20, 2016 at 09:15:14PM -0400, cov at codeaurora.org wrote:
> >> Hi Bjorn, Thomasz,
> >>
>
> >>
> >> Did you delete this because there were no current users, because you'd
> >> prefer users just use "-1", or for some other reason?
> >
> > I removed it because there were no users of it and, more importantly,
> > the code doesn't implement support for it.
>
> Is it possible to queue up Cov's patch as part of this effort once he
> rebases and sends an updated version? Cov will have to implement something
> else now.
I haven't see Cov's patch (patchwork doesn't follow URLs to git trees,
and I normally don't either). If they show up on the mailing list,
I'll take a look, of course.
Bjorn
^ permalink raw reply
* [PATCH] arm64: Correctly bounds check virt_addr_valid
From: Laura Abbott @ 2016-09-21 17:28 UTC (permalink / raw)
To: linux-arm-kernel
virt_addr_valid is supposed to return true if and only if virt_to_page
returns a valid page structure. The current macro does math on whatever
address is given and passes that to pfn_valid to verify. vmalloc and
module addresses can happen to generate a pfn that 'happens' to be
valid. Fix this by only performing the pfn_valid check on addresses that
have the potential to be valid.
Signed-off-by: Laura Abbott <labbott@redhat.com>
---
This caused a bug at least twice in hardened usercopy so it is an
actual problem. A further TODO is full DEBUG_VIRTUAL support to
catch these types of mistakes.
---
arch/arm64/include/asm/memory.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index 31b7322..f741e19 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -214,7 +214,7 @@ static inline void *phys_to_virt(phys_addr_t x)
#ifndef CONFIG_SPARSEMEM_VMEMMAP
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
-#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
+#define virt_addr_valid(kaddr) (((u64)kaddr) >= PAGE_OFFSET && pfn_valid(__pa(kaddr) >> PAGE_SHIFT))
#else
#define __virt_to_pgoff(kaddr) (((u64)(kaddr) & ~PAGE_OFFSET) / PAGE_SIZE * sizeof(struct page))
#define __page_to_voff(kaddr) (((u64)(page) & ~VMEMMAP_START) * PAGE_SIZE / sizeof(struct page))
@@ -222,8 +222,8 @@ static inline void *phys_to_virt(phys_addr_t x)
#define page_to_virt(page) ((void *)((__page_to_voff(page)) | PAGE_OFFSET))
#define virt_to_page(vaddr) ((struct page *)((__virt_to_pgoff(vaddr)) | VMEMMAP_START))
-#define virt_addr_valid(kaddr) pfn_valid((((u64)(kaddr) & ~PAGE_OFFSET) \
- + PHYS_OFFSET) >> PAGE_SHIFT)
+#define virt_addr_valid(kaddr) (((u64)kaddr) >= PAGE_OFFSET && pfn_valid((((u64)(kaddr) & ~PAGE_OFFSET) \
+ + PHYS_OFFSET) >> PAGE_SHIFT))
#endif
#endif
--
2.7.4
^ permalink raw reply related
* [PATCH v10 0/4] ACPI: parse the SPCR table
From: Aleksey Makarov @ 2016-09-21 17:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160921163823.GB9373@kroah.com>
On 09/21/2016 07:38 PM, Greg Kroah-Hartman wrote:
> On Wed, Sep 21, 2016 at 11:19:35AM -0500, Timur Tabi wrote:
>> On Wed, Sep 21, 2016 at 5:37 AM, Greg Kroah-Hartman
>> <gregkh@linuxfoundation.org> wrote:
>>>
>>> I thought you asked Rafael to take them, they are not in my queue
>>> anymore because of that. Don't try to shop-around for maintainers
>>> please, that's kind of rude...
>>
>> In Aleksey's defense, these patches have been floating around for far
>> too long, so I can understand his frustration.
>
> I understand the frustration as well, cross-maintainer messy patches
> like this are hard to get merged at times at they fall between the
> cracks a lot.
>
>> But as you said, I really hope Rafael picks these up for 4.9. We need
>> these patches for ARM server.
>
> I thought I saw an email saying he would do so, but it might have been
> for some other patchset, sorry...
This can be for "ACPI: ARM64: support for ACPI_TABLE_UPGRADE":
https://lkml.kernel.org/r/CAJZ5v0goXsQ2umcDXU0six+QtxcKGZq7mxhgxuvXTH2iZt6YNA at mail.gmail.com
As for this patchset, Rafael ACKed the ACPI part:
https://lkml.kernel.org/r/CAJZ5v0hdoLTfjrD8+WxSoxM48dqbZK2KwY_h+63kHKHKgO=JFA at mail.gmail.com
but I can not find any acknowledge from him that he is ready to merge the series.
On the contrary, he expressed his doubt that he is "the right maintainer to send this to":
https://lkml.kernel.org/r/CAJZ5v0gzjtFzig7nEumr83+J2dGb+OA8GNR2i45ZqznfV_hA-A at mail.gmail.com
I asked the ARM64 people, they said they prefer Rafael to do that:
https://lkml.kernel.org/r/20160909151758.GA11418 at arm.com
I interpreted the silence from Rafael as unwillingness to pull the series
and thought it's OK to ask you.
I am sorry if this looks rude for Rafael, I did not mean that.
Thank you
Aleksey Makarov
^ permalink raw reply
* [PATCH] usb: xhci: Fix the patch inherit dma configuration from
From: kbuild test robot @ 2016-09-21 17:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <DB5PR0401MB1925CDF6CBD45ACBEE95D152F5F60@DB5PR0401MB1925.eurprd04.prod.outlook.com>
Hi Sriram,
[auto build test ERROR on usb/usb-testing]
[also build test ERROR on v4.8-rc7 next-20160921]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]
url: https://github.com/0day-ci/linux/commits/Sriram-Dash/usb-xhci-Fix-the-patch-inherit-dma-configuration-from/20160922-004329
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: x86_64-randconfig-x012-201638 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All error/warnings (new ones prefixed by >>):
In file included from include/linux/list.h:8:0,
from include/linux/pci.h:25,
from drivers/usb/host/xhci.c:23:
drivers/usb/host/xhci.c: In function 'xhci_setup_msi':
>> drivers/usb/host/xhci.c:234:60: error: 'struct usb_bus' has no member named 'sysdev'
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
^
include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
^~~
>> drivers/usb/host/xhci.c:234:26: note: in expansion of macro 'to_pci_dev'
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
^~~~~~~~~~
drivers/usb/host/xhci.c: In function 'xhci_free_irq':
drivers/usb/host/xhci.c:260:59: error: 'struct usb_bus' has no member named 'sysdev'
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
^
include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
^~~
drivers/usb/host/xhci.c:260:25: note: in expansion of macro 'to_pci_dev'
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
^~~~~~~~~~
drivers/usb/host/xhci.c: In function 'xhci_setup_msix':
drivers/usb/host/xhci.c:283:45: error: 'struct usb_bus' has no member named 'sysdev'
struct pci_dev *pdev = to_pci_dev(hcd->self.sysdev);
^
include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
^~~
drivers/usb/host/xhci.c:283:25: note: in expansion of macro 'to_pci_dev'
struct pci_dev *pdev = to_pci_dev(hcd->self.sysdev);
^~~~~~~~~~
drivers/usb/host/xhci.c: In function 'xhci_cleanup_msix':
drivers/usb/host/xhci.c:338:45: error: 'struct usb_bus' has no member named 'sysdev'
struct pci_dev *pdev = to_pci_dev(hcd->self.sysdev);
^
include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
^~~
drivers/usb/host/xhci.c:338:25: note: in expansion of macro 'to_pci_dev'
struct pci_dev *pdev = to_pci_dev(hcd->self.sysdev);
^~~~~~~~~~
drivers/usb/host/xhci.c: In function 'xhci_try_enable_msi':
drivers/usb/host/xhci.c:377:43: error: 'struct usb_bus' has no member named 'sysdev'
pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
^
include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
^~~
drivers/usb/host/xhci.c:377:9: note: in expansion of macro 'to_pci_dev'
pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
^~~~~~~~~~
drivers/usb/host/xhci.c: In function 'xhci_shutdown':
drivers/usb/host/xhci.c:746:46: error: 'struct usb_bus' has no member named 'sysdev'
usb_disable_xhci_ports(to_pci_dev(hcd->self.sysdev));
^
include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
^~~
drivers/usb/host/xhci.c:746:26: note: in expansion of macro 'to_pci_dev'
usb_disable_xhci_ports(to_pci_dev(hcd->self.sysdev));
^~~~~~~~~~
drivers/usb/host/xhci.c:763:43: error: 'struct usb_bus' has no member named 'sysdev'
pci_set_power_state(to_pci_dev(hcd->self.sysdev), PCI_D3hot);
^
include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
^~~
drivers/usb/host/xhci.c:763:23: note: in expansion of macro 'to_pci_dev'
pci_set_power_state(to_pci_dev(hcd->self.sysdev), PCI_D3hot);
^~~~~~~~~~
drivers/usb/host/xhci.c: In function 'xhci_gen_setup':
drivers/usb/host/xhci.c:4835:33: error: 'struct usb_bus' has no member named 'sysdev'
struct device *dev = hcd->self.sysdev;
^
--
drivers/usb/host/xhci-mem.c: In function 'xhci_free_stream_ctx':
>> drivers/usb/host/xhci-mem.c:589:46: error: 'struct usb_bus' has no member named 'sysdev'
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
^
drivers/usb/host/xhci-mem.c: In function 'xhci_alloc_stream_ctx':
drivers/usb/host/xhci-mem.c:617:46: error: 'struct usb_bus' has no member named 'sysdev'
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
^
drivers/usb/host/xhci-mem.c: In function 'scratchpad_alloc':
drivers/usb/host/xhci-mem.c:1647:46: error: 'struct usb_bus' has no member named 'sysdev'
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
^
drivers/usb/host/xhci-mem.c: In function 'scratchpad_free':
drivers/usb/host/xhci-mem.c:1719:46: error: 'struct usb_bus' has no member named 'sysdev'
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
^
drivers/usb/host/xhci-mem.c: In function 'xhci_mem_cleanup':
drivers/usb/host/xhci-mem.c:1795:46: error: 'struct usb_bus' has no member named 'sysdev'
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
^
drivers/usb/host/xhci-mem.c: In function 'xhci_mem_init':
drivers/usb/host/xhci-mem.c:2337:46: error: 'struct usb_bus' has no member named 'sysdev'
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
^
vim +234 drivers/usb/host/xhci.c
228 /*
229 * Set up MSI
230 */
231 static int xhci_setup_msi(struct xhci_hcd *xhci)
232 {
233 int ret;
> 234 struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
235
236 ret = pci_enable_msi(pdev);
237 if (ret) {
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/gzip
Size: 21706 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160922/df321917/attachment-0001.gz>
^ permalink raw reply
* [PATCH 5/5] arm64: Add uprobe support
From: Catalin Marinas @ 2016-09-21 17:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160921110047.GA29470@localhost.localdomain>
On Wed, Sep 21, 2016 at 04:30:47PM +0530, Pratyush Anand wrote:
> On 20/09/2016:05:59:46 PM, Catalin Marinas wrote:
> > On Tue, Aug 02, 2016 at 11:00:09AM +0530, Pratyush Anand wrote:
> > > --- a/arch/arm64/include/asm/thread_info.h
> > > +++ b/arch/arm64/include/asm/thread_info.h
> > > @@ -109,6 +109,7 @@ static inline struct thread_info *current_thread_info(void)
> > > #define TIF_NEED_RESCHED 1
> > > #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */
> > > #define TIF_FOREIGN_FPSTATE 3 /* CPU's FP state is not current's */
> > > +#define TIF_UPROBE 5 /* uprobe breakpoint or singlestep */
> >
> > Nitpick: you can just use 4 until we cover this gap.
>
> Hummm..as stated in commit log, Shi Yang suggested to define TIF_UPROBE as 5 in
> stead of 4, since 4 has already been used in -rt kernel. May be, I can put a
> comment in code as well.
> Or, keep it 4 and -rt kernel will change their definitions. I am OK with both.
> let me know.
I forgot about the -rt kernel. I guess the -rt guys need to rebase the
patches anyway on top of mainline, so it's just a matter of sorting out
a minor conflict (as I already said, these bits are internal to the
kernel, so no ABI affected). For now, just use 4 so that we avoid
additional asm changes.
> > > --- a/arch/arm64/kernel/entry.S
> > > +++ b/arch/arm64/kernel/entry.S
> > > @@ -688,7 +688,8 @@ ret_fast_syscall:
> > > ldr x1, [tsk, #TI_FLAGS] // re-check for syscall tracing
> > > and x2, x1, #_TIF_SYSCALL_WORK
> > > cbnz x2, ret_fast_syscall_trace
> > > - and x2, x1, #_TIF_WORK_MASK
> > > + mov x2, #_TIF_WORK_MASK
> > > + and x2, x1, x2
> >
> > Is this needed because _TIF_WORK_MASK cannot work as an immediate value
> > to 'and'? We could reorder the TIF bits, they are not exposed to user to
> > have ABI implications.
>
> _TIF_WORK_MASK is defined as follows:
>
> #define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
> _TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \
> _TIF_UPROBE)
> Re-ordering will not help, because 0-3 have already been used by previous
> definitions. Only way to use immediate value could be if, TIF_UPROBE is defined
> as 4.
Yes, see above.
> > > +unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
> > > +{
> > > + return instruction_pointer(regs);
> > > +}
> > > +
> > > +int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
> > > + unsigned long addr)
> > > +{
> > > + probe_opcode_t insn;
> > > +
> > > + /* TODO: Currently we do not support AARCH32 instruction probing */
> >
> > Is there a way to check (not necessarily in this file) that we don't
> > probe 32-bit tasks?
>
> - Well, I do not have complete idea about it that, how it can be done. I think
> we can not check that just by looking a single bit in an instruction.
> My understanding is that, we can only know about it when we are executing the
> instruction, by reading pstate, but that would not be useful for uprobe
> instruction analysis.
>
> I hope, instruction encoding for aarch32 and aarch64 are different, and by
> analyzing for all types of aarch32 instructions, we will be able to decide
> that whether instruction is 32 bit trace-able or not. Accordingly, we can use
> either BRK or BKPT instruction for breakpoint generation.
We may have some unrelated instruction encoding overlapping but I
haven't checked. I was more thinking about whether we know which task is
being probed and check is_compat_task() or maybe using
compat_user_mode(regs).
--
Catalin
^ permalink raw reply
* [PATCH v4 2/5] ARM: dts: imx6q: Add Engicam i.CoreM6 Quad/Dual initial support
From: Fabio Estevam @ 2016-09-21 16:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOf5uw=bMMa98RP=kvA=NfS89TWOEqU3Q2HjpEmzUTVrdg2=sQ@mail.gmail.com>
Hi Michael,
On Tue, Sep 20, 2016 at 10:23 AM, Michael Trimarchi
<michael@amarulasolutions.com> wrote:
> Engicam use fixed regulator always on and on on boot. Their board does
> not have any
> external pmu. Is this answer to your comment?
All I am saying is that "regulator-boot-on" and "regulator-always-on"
properties should be removed.
The "reg_3p3v" regulator will be turned on and off by the flexcan driver.
^ permalink raw reply
* [PATCH] arm64, numa: Add cpu_to_node() implementation.
From: Jon Masters @ 2016-09-21 16:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <57E143DA.5030602@linaro.org>
On 09/20/2016 10:12 AM, Hanjun Guo wrote:
> On 09/20/2016 09:38 PM, Robert Richter wrote:
>> On 20.09.16 19:32:34, Hanjun Guo wrote:
>>> On 09/20/2016 06:43 PM, Robert Richter wrote:
>>
>>>> Instead we need to make sure the set_*numa_node() functions are called
>>>> earlier before secondary cpus are booted. My suggested change for that
>>>> is this:
>>>>
>>>>
>>>> diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
>>>> index d93d43352504..952365c2f100 100644
>>>> --- a/arch/arm64/kernel/smp.c
>>>> +++ b/arch/arm64/kernel/smp.c
>>>> @@ -204,7 +204,6 @@ int __cpu_up(unsigned int cpu, struct
>>>> task_struct *idle)
>>>> static void smp_store_cpu_info(unsigned int cpuid)
>>>> {
>>>> store_cpu_topology(cpuid);
>>>> - numa_store_cpu_info(cpuid);
>>>> }
>>>>
>>>> /*
>>>> @@ -719,6 +718,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
>>>> continue;
>>>>
>>>> set_cpu_present(cpu, true);
>>>> + numa_store_cpu_info(cpu);
>>>> }
>>>> }
>>>
>>> We tried a similar approach which add numa_store_cpu_info() in
>>> early_map_cpu_to_node(), and remove it from smp_store_cpu_info,
>>> but didn't work for us, we will try your approach to see if works.
>
> And it works :)
Great. I'm curious for further (immediate) feedback on David's updated
patch in the other thread due to some time sensitive needs on our end.
Jon.
--
Computer Architect | Sent from my Fedora powered laptop
^ permalink raw reply
* [PATCH v10 0/4] ACPI: parse the SPCR table
From: Greg Kroah-Hartman @ 2016-09-21 16:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOZdJXV2KyZAbrRxQ_zmFNd3Gn4vyuqSsUfJMTU8wpiOtjyE=g@mail.gmail.com>
On Wed, Sep 21, 2016 at 11:19:35AM -0500, Timur Tabi wrote:
> On Wed, Sep 21, 2016 at 5:37 AM, Greg Kroah-Hartman
> <gregkh@linuxfoundation.org> wrote:
> >
> > I thought you asked Rafael to take them, they are not in my queue
> > anymore because of that. Don't try to shop-around for maintainers
> > please, that's kind of rude...
>
> In Aleksey's defense, these patches have been floating around for far
> too long, so I can understand his frustration.
I understand the frustration as well, cross-maintainer messy patches
like this are hard to get merged at times at they fall between the
cracks a lot.
> But as you said, I really hope Rafael picks these up for 4.9. We need
> these patches for ARM server.
I thought I saw an email saying he would do so, but it might have been
for some other patchset, sorry...
greg k-h
^ permalink raw reply
* [PATCH V3 2/4] ARM64 LPC: LPC driver implementation on Hip06
From: Gabriele Paoloni @ 2016-09-21 16:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <e246c886-e0eb-b635-01ae-f00bbfcfb56b@gmail.com>
Hi Zhichang
> -----Original Message-----
> From: zhichang [mailto:zhichang.yuan02 at gmail.com]
> Sent: 21 September 2016 11:09
> To: Arnd Bergmann; linux-arm-kernel at lists.infradead.org
> Cc: Gabriele Paoloni; devicetree at vger.kernel.org;
> lorenzo.pieralisi at arm.com; minyard at acm.org; linux-pci at vger.kernel.org;
> gregkh at linuxfoundation.org; John Garry; will.deacon at arm.com; linux-
> kernel at vger.kernel.org; Yuanzhichang; Linuxarm; xuwei (O); linux-
> serial at vger.kernel.org; benh at kernel.crashing.org;
> zourongrong at gmail.com; liviu.dudau at arm.com; kantyzc at 163.com
> Subject: Re: [PATCH V3 2/4] ARM64 LPC: LPC driver implementation on
> Hip06
>
> Hi, Arnd,
>
>
>
> On 2016?09?15? 20:24, Arnd Bergmann wrote:
> > On Thursday, September 15, 2016 12:05:51 PM CEST Gabriele Paoloni
> wrote:
> >>> -----Original Message-----
> >>> On Thursday, September 15, 2016 8:02:27 AM CEST Gabriele Paoloni
> wrote:
> >>>>
> >>>> From <<3.1.1. Open Firmware Properties for Bus Nodes>> in
> >>>> http://www.firmware.org/1275/bindings/isa/isa0_4d.ps
> >>>>
> >>>> I quote:
> >>>> "There shall be an entry in the "ranges" property for each
> >>>> of the Memory and/or I/O spaces if that address space is
> >>>> mapped through the bridge."
> >>>>
> >>>> It seems to me that it is ok to have 1:1 address mapping and that
> >>>> therefore of_translate_address() should fail if "ranges" is not
> >>>> present.
> >>>
> >>> The key here is the definition of "mapped through the bridge".
> >>> I can only understand this as "directly mapped", i.e. an I/O
> >>> port of the child bus corresponds directly to a memory address
> >>> on the parent bus, but this is not the case here.
> >>>
> >>> The problem with adding the mapping here is that it looks
> >>> like it should be valid to create a page table entry for
> >>> the address returned from the translation and access it through
> >>> a pointer dereference, but that is clearly not possible.
> >>
> >> I understand that somehow we are abusing of the ranges property
> >> here however the point is that with the current implementation
> ranges
> >> is needed because otherwise the ipmi driver probe will fail here:
> >>
> >> of_ipmi_probe -> of_address_to_resource -> __of_address_to_resource
> >> -> of_translate_address -> __of_translate_address
> >>
> >> Now we had a bit of discussion internally and to avoid
> >> having ranges we came up with two possible solutions:
> >>
> >> 1) Using bit 3 of phys.hi cell in 2.2.1 of
> >> http://www.firmware.org/1275/bindings/isa/isa0_4d.ps
> >> This would mean reworking of_bus_isa_get_flags in
> >> http://lxr.free-electrons.com/source/drivers/of/address.c#L398
> >> and setting a new flag to be checked in __of_address_to_resource
> >>
> >> 2) Adding a property in the bindings of each device that is
> >> a child of our LPC bus and modify __of_address_to_resource
> >> to check if the property is in the DT and eventually bypass
> >> of_translate_address
> >>
> >> However in both 1) and 2) there are some issues:
> >> in 1) we are not complying with the isa binding doc (we use
> >> a bit that should be zero); in 2) we need to modify the
> >> bindings documentation of the devices that are connected
> >> to our LPC controller (therefore modifying other devices
> >> bindings to fit our special case).
> >>
> >> I think that maybe having the 1:1 range mapping doesn't
> >> reflect well the reality but it is the less painful
> >> solution...
> >>
> >> What's your view?
> >
> > We can check the 'i' bit for I/O space in of_bus_isa_get_flags,
> > and that should be enough to translate the I/O port number.
> >
> > The only part we need to change here is to not go through
> > the crazy conversion all the way from PCI I/O space to a
> > physical address and back to a (logical) port number
> > that we do today with of_translate_address/pci_address_to_pio.
> >
> Sorry for the late response! Several days' leave....
> Do you want to bypass of_translate_address and pci_address_to_pio for
> the registered specific PIO?
> I think the bypass for of_translate_address is ok, but worry some new
> issues will emerge without the
> conversion between physical address and logical/linux port number.
>
> When PCI host bridge which support IO operations is configured and
> enabled, the pci_address_to_pio will
> populate the logical IO range from ZERO for the first host bridge. Our
> LPC will also use part of the IO range
> started from ZERO. It will make in/out enter the wrong branch possibly.
>
> In V2, the 0 - 0x1000 logical IO range is reserved for LPC use only.
> But it seems not so good. In this way,
> PCI has no chance to use low 4K IO range(logical).
>
> So, in V3, applying the conversion from physical/cpu address to
> logical/linux IO port for any IO ranges,
> including the LPC, but recorded the logical IO range for LPC. When
> calling in/out with a logical port address,
> we can check this port fall into LPC logical IO range and get back the
> real IO.
>
> Do you have further comments about this??
I think there are two separate issues to be discussed:
The first issue is about having of_translate_address failing due to
"range" missing. About this Arnd suggested that it is not appropriate
to have a range describing a bridge 1:1 mapping and this was discussed
before in this thread. Arnd had a suggestion about this (see below)
however (looking twice at the code) it seems to me that such solution
would lead to quite some duplication from __of_translate_address()
in order to retrieve the actual addr from dt...
I think extending of_empty_ranges_quirk() may be a reasonable solution.
What do you think Arnd?
The second issue is a conflict between cpu addresses used by the LPC
controller and i/o tokens from pci endpoints.
About this what if we modify armn64_extio_ops to have a list of ranges
rather than only one range (now we have just start/end); then in the
LPC driver we can scan the LPC child devices and
1) populate such list of ranges
2) call pci_register_io_range for such ranges
Then when calling __of_address_to_resource we retrieve I/O tokens
for the devices on top of the LPC driver and in the I/O accessors
we call pci_pio_to_address to figure out the cpu address and compare
it to the list of ranges in armn64_extio_ops.
What about this?
Thanks
Gab
>
>
> > I can think of a several of ways to fix __of_address_to_resource
> > to just do the right thing according to the ISA binding to
> > make the normal drivers work.
> >
> > The easiest solution is probably to hook into the
> > "taddr == OF_BAD_ADDR" case in __of_address_to_resource
> > and add a lookup for ISA buses there, and instead check
> > if some special I/O port operations were registered
> > for the port number, using an architecture specific
> > function that arm64 implements. Other architectures
> > like x86 that don't have a direct mapping between I/O
> > ports and MMIO addresses would implement that same
> > function differently.
>
> What about add the specific quirk for Hip06 LPC in
> of_empty_ranges_quirk()??
>
> you know, there are several cases in which of_translate_address return
> OF_BAD_ADDR.
> And if we only check the special port range, it seems a bit risky. If
> some device want to use this port range
> when no hip06 LPC is configured, the checking does not work. I think we
> should also check the relevant device.
>
>
> Best,
> Zhichang
>
>
> >
> > Arnd
> >
^ permalink raw reply
* [PATCH v10 0/4] ACPI: parse the SPCR table
From: Timur Tabi @ 2016-09-21 16:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160921103749.GA7993@kroah.com>
On Wed, Sep 21, 2016 at 5:37 AM, Greg Kroah-Hartman
<gregkh@linuxfoundation.org> wrote:
>
> I thought you asked Rafael to take them, they are not in my queue
> anymore because of that. Don't try to shop-around for maintainers
> please, that's kind of rude...
In Aleksey's defense, these patches have been floating around for far
too long, so I can understand his frustration. But as you said, I
really hope Rafael picks these up for 4.9. We need these patches for
ARM server.
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH 4/4] ARM: dts: am4372: add DMA properties for tscadc
From: Mugunthan V N @ 2016-09-21 16:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160921161134.6951-1-mugunthanvnm@ti.com>
Add DMA properties for tscadc
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
arch/arm/boot/dts/am4372.dtsi | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index 0fadae5..6094d17 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -867,6 +867,8 @@
clocks = <&adc_tsc_fck>;
clock-names = "fck";
status = "disabled";
+ dmas = <&edma 53 0>, <&edma 57 0>;
+ dma-names = "fifo0", "fifo1";
tsc {
compatible = "ti,am3359-tsc";
--
2.10.0.129.g35f6318
^ permalink raw reply related
* [PATCH 3/4] ARM: dts: am33xx: add DMA properties for tscadc
From: Mugunthan V N @ 2016-09-21 16:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160921161134.6951-1-mugunthanvnm@ti.com>
Add DMA properties for tscadc
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
arch/arm/boot/dts/am33xx.dtsi | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 98748c6..6d607b8 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -917,6 +917,8 @@
interrupts = <16>;
ti,hwmods = "adc_tsc";
status = "disabled";
+ dmas = <&edma 53 0>, <&edma 57 0>;
+ dma-names = "fifo0", "fifo1";
tsc {
compatible = "ti,am3359-tsc";
--
2.10.0.129.g35f6318
^ permalink raw reply related
* [PATCH 2/4] drivers: iio: ti_am335x_adc: add dma support
From: Mugunthan V N @ 2016-09-21 16:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160921161134.6951-1-mugunthanvnm@ti.com>
This patch adds the required pieces to ti_am335x_adc driver for
DMA support
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
drivers/iio/adc/ti_am335x_adc.c | 160 ++++++++++++++++++++++++++++++++++-
include/linux/mfd/ti_am335x_tscadc.h | 7 ++
2 files changed, 164 insertions(+), 3 deletions(-)
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index c3cfacca..89d0b07 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -30,10 +30,32 @@
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+
+#define DMA_BUFFER_SIZE SZ_2K
+
+struct tiadc_dma {
+ /* Filter function */
+ dma_filter_fn fn;
+ /* Parameter to the filter function */
+ void *param;
+ struct dma_slave_config conf;
+ struct dma_chan *chan;
+ dma_addr_t addr;
+ dma_cookie_t cookie;
+ u8 *buf;
+ bool valid_buf_seg;
+ int buf_offset;
+ u8 fifo_thresh;
+};
+
struct tiadc_device {
struct ti_tscadc_dev *mfd_tscadc;
+ struct tiadc_dma dma;
struct mutex fifo1_lock; /* to protect fifo access */
int channels;
+ int total_ch_enabled;
u8 channel_line[8];
u8 channel_step[8];
int buffer_en_ch_steps;
@@ -184,6 +206,7 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
u16 *data = adc_dev->data;
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+
for (k = 0; k < fifo1count; k = k + i) {
for (i = 0; i < (indio_dev->scan_bytes)/2; i++) {
read = tiadc_readl(adc_dev, REG_FIFO1);
@@ -198,6 +221,68 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
return IRQ_HANDLED;
}
+static void tiadc_dma_rx_complete(void *param)
+{
+ struct iio_dev *indio_dev = param;
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ u8 *data;
+ int i;
+
+ data = dma->valid_buf_seg ? dma->buf + dma->buf_offset : dma->buf;
+ dma->valid_buf_seg = !dma->valid_buf_seg;
+
+ for (i = 0; i < dma->buf_offset; i += indio_dev->scan_bytes) {
+ iio_push_to_buffers(indio_dev, data);
+ data += indio_dev->scan_bytes;
+ }
+}
+
+static int tiadc_start_dma(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ struct dma_async_tx_descriptor *desc;
+
+ dma->valid_buf_seg = false;
+ dma->fifo_thresh = FIFO1_THRESHOLD;
+ /*
+ * Make the fifo thresh as the multiple of total number of
+ * channels enabled, so make sure that that cyclic DMA period
+ * length is also a multiple of total number of channels
+ * enabled. This ensures that no invalid data is reported
+ * to the stack via iio_push_to_buffers().
+ */
+ dma->fifo_thresh -= (dma->fifo_thresh + 1) % adc_dev->total_ch_enabled;
+ dma->buf_offset = DMA_BUFFER_SIZE / 2;
+ /* Make sure that period length is multiple of fifo thresh level */
+ dma->buf_offset -= dma->buf_offset % ((dma->fifo_thresh + 1) *
+ sizeof(u16));
+
+ dma->conf.src_maxburst = dma->fifo_thresh + 1;
+ dmaengine_slave_config(dma->chan, &dma->conf);
+
+ desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
+ dma->buf_offset * 2,
+ dma->buf_offset, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc)
+ return -EBUSY;
+
+ desc->callback = tiadc_dma_rx_complete;
+ desc->callback_param = indio_dev;
+
+ dma->cookie = dmaengine_submit(desc);
+
+ dma_async_issue_pending(dma->chan);
+
+ tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
+ tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
+ tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
+
+ return 0;
+}
+
static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
@@ -218,20 +303,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
unsigned int enb = 0;
u8 bit;
+ u32 irq_enable;
tiadc_step_config(indio_dev);
- for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
+ for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
enb |= (get_adc_step_bit(adc_dev, bit) << 1);
+ adc_dev->total_ch_enabled++;
+ }
adc_dev->buffer_en_ch_steps = enb;
+ if (dma->chan)
+ tiadc_start_dma(indio_dev);
+
am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
- tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
- | IRQENB_FIFO1OVRRUN);
+
+ irq_enable = IRQENB_FIFO1OVRRUN;
+ if (!dma->chan)
+ irq_enable |= IRQENB_FIFO1THRES;
+ tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable);
return 0;
}
@@ -239,12 +334,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
int fifo1count, i, read;
tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
adc_dev->buffer_en_ch_steps = 0;
+ adc_dev->total_ch_enabled = 0;
+ if (dma->chan) {
+ tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
+ dmaengine_terminate_async(dma->chan);
+ }
/* Flush FIFO of leftover data in the time it takes to disable adc */
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
@@ -430,6 +531,50 @@ static const struct iio_info tiadc_info = {
.driver_module = THIS_MODULE,
};
+static bool the_no_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+ return false;
+}
+
+static int tiadc_request_dma(struct platform_device *pdev,
+ struct tiadc_device *adc_dev)
+{
+ struct tiadc_dma *dma = &adc_dev->dma;
+ dma_cap_mask_t mask;
+
+ dma->fn = the_no_dma_filter_fn;
+
+ /* Default slave configuration parameters */
+ dma->conf.direction = DMA_DEV_TO_MEM;
+ dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_CYCLIC, mask);
+
+ /* Get a channel for RX */
+ dma->chan = dma_request_slave_channel_compat(mask,
+ dma->fn, dma->param,
+ adc_dev->mfd_tscadc->dev,
+ "fifo1");
+ if (!dma->chan)
+ return -ENODEV;
+
+ /* RX buffer */
+ dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
+ &dma->addr, GFP_KERNEL);
+ if (!dma->buf)
+ goto err;
+
+ dev_dbg_ratelimited(adc_dev->mfd_tscadc->dev, "got dma channel\n");
+
+ return 0;
+err:
+ dma_release_channel(dma->chan);
+
+ return -ENOMEM;
+}
+
static int tiadc_parse_dt(struct platform_device *pdev,
struct tiadc_device *adc_dev)
{
@@ -512,8 +657,14 @@ static int tiadc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, indio_dev);
+ err = tiadc_request_dma(pdev, adc_dev);
+ if (err && err != -ENODEV)
+ goto err_dma;
+
return 0;
+err_dma:
+ iio_device_unregister(indio_dev);
err_buffer_unregister:
tiadc_iio_buffered_hardware_remove(indio_dev);
err_free_channels:
@@ -525,8 +676,11 @@ static int tiadc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
u32 step_en;
+ if (dma->chan)
+ dma_release_channel(dma->chan);
iio_device_unregister(indio_dev);
tiadc_iio_buffered_hardware_remove(indio_dev);
tiadc_channels_remove(indio_dev);
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index e45a208..fb9dc99 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -23,6 +23,8 @@
#define REG_IRQENABLE 0x02C
#define REG_IRQCLR 0x030
#define REG_IRQWAKEUP 0x034
+#define REG_DMAENABLE_SET 0x038
+#define REG_DMAENABLE_CLEAR 0x038
#define REG_CTRL 0x040
#define REG_ADCFSM 0x044
#define REG_CLKDIV 0x04C
@@ -36,6 +38,7 @@
#define REG_FIFO0THR 0xE8
#define REG_FIFO1CNT 0xF0
#define REG_FIFO1THR 0xF4
+#define REG_DMA1REQ 0xF8
#define REG_FIFO0 0x100
#define REG_FIFO1 0x200
@@ -126,6 +129,10 @@
#define FIFOREAD_DATA_MASK (0xfff << 0)
#define FIFOREAD_CHNLID_MASK (0xf << 16)
+/* DMA ENABLE/CLEAR Register */
+#define DMA_FIFO0 BIT(0)
+#define DMA_FIFO1 BIT(1)
+
/* Sequencer Status */
#define SEQ_STATUS BIT(5)
#define CHARGE_STEP 0x11
--
2.10.0.129.g35f6318
^ permalink raw reply related
* [PATCH 1/4] mfd: ti_am335x_tscadc: store physical address
From: Mugunthan V N @ 2016-09-21 16:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160921161134.6951-1-mugunthanvnm@ti.com>
store the physical address of the device in its priv to use it
for DMA addressing in the client drivers.
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
drivers/mfd/ti_am335x_tscadc.c | 1 +
include/linux/mfd/ti_am335x_tscadc.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index c8f027b..0f3fab4 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -183,6 +183,7 @@ static int ti_tscadc_probe(struct platform_device *pdev)
tscadc->irq = err;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ tscadc->tscadc_phys_base = res->start;
tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tscadc->tscadc_base))
return PTR_ERR(tscadc->tscadc_base);
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index 7f55b8b..e45a208 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -155,6 +155,7 @@ struct ti_tscadc_dev {
struct device *dev;
struct regmap *regmap;
void __iomem *tscadc_base;
+ phys_addr_t tscadc_phys_base;
int irq;
int used_cells; /* 1-2 */
int tsc_wires;
--
2.10.0.129.g35f6318
^ permalink raw reply related
* [PATCH 0/4] Add DMA support for ti_am335x_adc driver
From: Mugunthan V N @ 2016-09-21 16:11 UTC (permalink / raw)
To: linux-arm-kernel
The ADC has a 64 work depth fifo length which holds the ADC data
till the CPU reads. So when a user program needs a large ADC data
to operate on, then it has to do multiple reads to get its
buffer. Currently if the application asks for 4 samples per
channel with all 8 channels are enabled, kernel can provide only
3 samples per channel when all 8 channels are enabled (logs at
[1]). So with DMA support user can request for large number of
samples at a time (logs at [2]).
Tested the patch on AM437x-gp-evm and AM335x Boneblack with the
patch [3] to enable ADC and pushed a branch for testing [4]
[1] - http://pastebin.ubuntu.com/23211490/
[2] - http://pastebin.ubuntu.com/23211492/
[3] - http://pastebin.ubuntu.com/23211494/
[4] - git://git.ti.com/~mugunthanvnm/ti-linux-kernel/linux.git iio-dma
Mugunthan V N (4):
mfd: ti_am335x_tscadc: store physical address
drivers: iio: ti_am335x_adc: add dma support
ARM: dts: am33xx: add DMA properties for tscadc
ARM: dts: am4372: add DMA properties for tscadc
arch/arm/boot/dts/am33xx.dtsi | 2 +
arch/arm/boot/dts/am4372.dtsi | 2 +
drivers/iio/adc/ti_am335x_adc.c | 160 ++++++++++++++++++++++++++++++++++-
drivers/mfd/ti_am335x_tscadc.c | 1 +
include/linux/mfd/ti_am335x_tscadc.h | 8 ++
5 files changed, 170 insertions(+), 3 deletions(-)
--
2.10.0.129.g35f6318
^ permalink raw reply
* [PATCH 2/2] MAINTAINERS: add ARM and arm64 EFI specific files to EFI subsystem
From: Will Deacon @ 2016-09-21 15:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474472114-12973-2-git-send-email-ard.biesheuvel@linaro.org>
On Wed, Sep 21, 2016 at 04:35:14PM +0100, Ard Biesheuvel wrote:
> Since I will be co-maintaining the EFI subsystem, it makes sense to
> mention the ARM and arm64 EFI bits in the EFI section in MAINTAINERS
> so that Matt, the list and I get cc'ed on proposed changes.
>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: Russell King <rmk+kernel@armlinux.org.uk>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
> MAINTAINERS | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 224518556a84..cc8b36699f94 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4562,12 +4562,14 @@ L: linux-efi at vger.kernel.org
> T: git git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git
> S: Maintained
> F: Documentation/efi-stub.txt
> -F: arch/ia64/kernel/efi.c
> +F: arch/*/kernel/efi.c
> F: arch/x86/boot/compressed/eboot.[ch]
> -F: arch/x86/include/asm/efi.h
> +F: arch/*/include/asm/efi.h
> F: arch/x86/platform/efi/
> F: drivers/firmware/efi/
> F: include/linux/efi*.h
> +F: arch/arm/boot/compressed/efi-header.S
> +F: arch/arm64/kernel/efi-entry.S
For the arm64 bit,
Acked-by: Will Deacon <will.deacon@arm.com>
Thanks,
Will
^ permalink raw reply
* [PATCH 1/2] MAINTAINERS: add myself as EFI maintainer
From: Matt Fleming @ 2016-09-21 15:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474472114-12973-1-git-send-email-ard.biesheuvel@linaro.org>
On Wed, 21 Sep, at 04:35:13PM, Ard Biesheuvel wrote:
> At the request of Matt, I am taking up co-maintainership of the EFI
> subsystem. So add my name to the EFI section in MAINTAINERS, and
> change the SCM tree reference to point to the new shared Git repo.
>
> Cc: Matt Fleming <matt@codeblueprint.co.uk>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
> MAINTAINERS | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
Acked-by: Matt Fleming <matt@codeblueprint.co.uk>
^ permalink raw reply
* [PATCH 1/2] MAINTAINERS: add myself as EFI maintainer
From: Will Deacon @ 2016-09-21 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474472114-12973-1-git-send-email-ard.biesheuvel@linaro.org>
On Wed, Sep 21, 2016 at 04:35:13PM +0100, Ard Biesheuvel wrote:
> At the request of Matt, I am taking up co-maintainership of the EFI
> subsystem. So add my name to the EFI section in MAINTAINERS, and
> change the SCM tree reference to point to the new shared Git repo.
>
> Cc: Matt Fleming <matt@codeblueprint.co.uk>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
> MAINTAINERS | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
I don't know if "congratulations" is the right word to use, but:
Acked-by: Will Deacon <will.deacon@arm.com>
Will
^ permalink raw reply
* [PATCH 2/2] MAINTAINERS: add ARM and arm64 EFI specific files to EFI subsystem
From: Ard Biesheuvel @ 2016-09-21 15:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474472114-12973-1-git-send-email-ard.biesheuvel@linaro.org>
Since I will be co-maintaining the EFI subsystem, it makes sense to
mention the ARM and arm64 EFI bits in the EFI section in MAINTAINERS
so that Matt, the list and I get cc'ed on proposed changes.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
MAINTAINERS | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 224518556a84..cc8b36699f94 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4562,12 +4562,14 @@ L: linux-efi at vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git
S: Maintained
F: Documentation/efi-stub.txt
-F: arch/ia64/kernel/efi.c
+F: arch/*/kernel/efi.c
F: arch/x86/boot/compressed/eboot.[ch]
-F: arch/x86/include/asm/efi.h
+F: arch/*/include/asm/efi.h
F: arch/x86/platform/efi/
F: drivers/firmware/efi/
F: include/linux/efi*.h
+F: arch/arm/boot/compressed/efi-header.S
+F: arch/arm64/kernel/efi-entry.S
EFI VARIABLE FILESYSTEM
M: Matthew Garrett <matthew.garrett@nebula.com>
--
2.7.4
^ permalink raw reply related
* [PATCH 1/2] MAINTAINERS: add myself as EFI maintainer
From: Ard Biesheuvel @ 2016-09-21 15:35 UTC (permalink / raw)
To: linux-arm-kernel
At the request of Matt, I am taking up co-maintainership of the EFI
subsystem. So add my name to the EFI section in MAINTAINERS, and
change the SCM tree reference to point to the new shared Git repo.
Cc: Matt Fleming <matt@codeblueprint.co.uk>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
MAINTAINERS | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 007d05acbb5f..224518556a84 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4557,8 +4557,9 @@ F: sound/usb/misc/ua101.c
EXTENSIBLE FIRMWARE INTERFACE (EFI)
M: Matt Fleming <matt@codeblueprint.co.uk>
+M: Ard Biesheuvel <ard.biesheuvel@linaro.org>
L: linux-efi at vger.kernel.org
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/efi.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git
S: Maintained
F: Documentation/efi-stub.txt
F: arch/ia64/kernel/efi.c
--
2.7.4
^ permalink raw reply related
* [PATCH -next] soc: mediatek: PMIC wrap: fix missing clk_disable_unprepare() on error in pwrap_probe()
From: Wei Yongjun @ 2016-09-21 15:08 UTC (permalink / raw)
To: linux-arm-kernel
From: Wei Yongjun <weiyongjun1@huawei.com>
Fix the missing clk_disable_unprepare() before return
from pwrap_probe() in the error handling case.
Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
---
drivers/soc/mediatek/mtk-pmic-wrap.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
index a5f1093..4da3bb2 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -1200,7 +1200,8 @@ static int pwrap_probe(struct platform_device *pdev)
if (!(pwrap_readl(wrp, PWRAP_WACS2_RDATA) & PWRAP_STATE_INIT_DONE0)) {
dev_dbg(wrp->dev, "initialization isn't finished\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_out2;
}
/* Initialize watchdog, may not be done by the bootloader */
^ permalink raw reply related
* [PATCH v5] soc: qcom: add l2 cache perf events driver
From: Neil Leeder @ 2016-09-21 15:05 UTC (permalink / raw)
To: linux-arm-kernel
Adds perf events support for L2 cache PMU.
The L2 cache PMU driver is named 'l2cache_0' and can be used
with perf events to profile L2 events such as cache hits
and misses.
Signed-off-by: Neil Leeder <nleeder@codeaurora.org>
---
v5:
Fold the header and l2-accessors into .c file
Use multi-instance framework for hotplug
Change terminology from slice to cluster for clarity
Remove unnecessary rmw sequence for enable registers
Use prev_count in hwc rather than in slice
Enforce all events in same group on same CPU
Add comments, rename variables for clarity
v4:
Replace notifier with hotplug statemachine
Allocate PMU struct dynamically
v3:
Remove exports from l2-accessors
Change l2-accessors Kconfig to make it not user-selectable
Reorder and remove unnecessary includes
v2:
Add the l2-accessors patch to this patchset, previously posted separately.
Remove sampling and per-task functionality for this uncore PMU.
Use cpumask to replace code which filtered events to one cpu per slice.
Replace manual event filtering with filter_match callback.
Use a separate used_mask for event groups.
Add hotplug notifier for CPU and irq migration.
Remove extraneous synchronisation instructions.
Other miscellaneous cleanup.
drivers/soc/qcom/Kconfig | 9 +
drivers/soc/qcom/Makefile | 1 +
drivers/soc/qcom/perf_event_l2.c | 948 +++++++++++++++++++++++++++++++++++++++
include/linux/cpuhotplug.h | 1 +
4 files changed, 959 insertions(+)
create mode 100644 drivers/soc/qcom/perf_event_l2.c
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 461b387..4c32646 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -10,6 +10,15 @@ config QCOM_GSBI
functions for connecting the underlying serial UART, SPI, and I2C
devices to the output pins.
+config QCOM_PERF_EVENTS_L2
+ bool "Qualcomm Technologies L2-cache perf events"
+ depends on ARCH_QCOM && HW_PERF_EVENTS
+ help
+ Provides support for the L2 cache performance monitor unit (PMU)
+ in Qualcomm Technologies processors.
+ Adds the L2 cache PMU into the perf events subsystem for
+ monitoring L2 cache events.
+
config QCOM_PM
bool "Qualcomm Power Management"
depends on ARCH_QCOM && !ARM64
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index fdd664e..4c9df3b 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
+obj-$(CONFIG_QCOM_PERF_EVENTS_L2) += perf_event_l2.o
obj-$(CONFIG_QCOM_PM) += spm.o
obj-$(CONFIG_QCOM_SMD) += smd.o
obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
diff --git a/drivers/soc/qcom/perf_event_l2.c b/drivers/soc/qcom/perf_event_l2.c
new file mode 100644
index 0000000..bbf47c9
--- /dev/null
+++ b/drivers/soc/qcom/perf_event_l2.c
@@ -0,0 +1,948 @@
+/* Copyright (c) 2015,2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/perf_event.h>
+#include <linux/platform_device.h>
+
+#define MAX_L2_CTRS 9
+
+#define L2PMCR_NUM_EV_SHIFT 11
+#define L2PMCR_NUM_EV_MASK 0x1F
+
+#define L2PMCR 0x400
+#define L2PMCNTENCLR 0x403
+#define L2PMCNTENSET 0x404
+#define L2PMINTENCLR 0x405
+#define L2PMINTENSET 0x406
+#define L2PMOVSCLR 0x407
+#define L2PMOVSSET 0x408
+#define L2PMCCNTCR 0x409
+#define L2PMCCNTR 0x40A
+#define L2PMCCNTSR 0x40C
+#define L2PMRESR 0x410
+#define IA_L2PMXEVCNTCR_BASE 0x420
+#define IA_L2PMXEVCNTR_BASE 0x421
+#define IA_L2PMXEVFILTER_BASE 0x423
+#define IA_L2PMXEVTYPER_BASE 0x424
+
+#define IA_L2_REG_OFFSET 0x10
+
+#define L2PMXEVFILTER_SUFILTER_ALL 0x000E0000
+#define L2PMXEVFILTER_ORGFILTER_IDINDEP 0x00000004
+#define L2PMXEVFILTER_ORGFILTER_ALL 0x00000003
+
+#define L2PM_CC_ENABLE 0x80000000
+
+#define L2EVTYPER_REG_SHIFT 3
+
+#define L2PMRESR_GROUP_BITS 8
+#define L2PMRESR_GROUP_MASK GENMASK(7, 0)
+
+#define L2CYCLE_CTR_BIT 31
+#define L2CYCLE_CTR_RAW_CODE 0xFE
+
+#define L2PMCR_RESET_ALL 0x6
+#define L2PMCR_COUNTERS_ENABLE 0x1
+#define L2PMCR_COUNTERS_DISABLE 0x0
+
+#define L2PMRESR_EN ((u64)1 << 63)
+
+#define L2_EVT_MASK 0x00000FFF
+#define L2_EVT_CODE_MASK 0x00000FF0
+#define L2_EVT_GRP_MASK 0x0000000F
+#define L2_EVT_CODE_SHIFT 4
+#define L2_EVT_GRP_SHIFT 0
+
+#define L2_EVT_CODE(event) (((event) & L2_EVT_CODE_MASK) >> L2_EVT_CODE_SHIFT)
+#define L2_EVT_GROUP(event) (((event) & L2_EVT_GRP_MASK) >> L2_EVT_GRP_SHIFT)
+
+#define L2_EVT_GROUP_MAX 7
+
+#define L2_MAX_PERIOD U32_MAX
+#define L2_CNT_PERIOD (U32_MAX - GENMASK(26, 0))
+
+#define L2CPUSRSELR_EL1 S3_3_c15_c0_6
+#define L2CPUSRDR_EL1 S3_3_c15_c0_7
+
+static DEFINE_RAW_SPINLOCK(l2_access_lock);
+
+/**
+ * set_l2_indirect_reg: write value to an L2 register
+ * @reg: Address of L2 register.
+ * @value: Value to be written to register.
+ *
+ * Use architecturally required barriers for ordering between system register
+ * accesses
+ */
+static void set_l2_indirect_reg(u64 reg, u64 val)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&l2_access_lock, flags);
+ write_sysreg(reg, L2CPUSRSELR_EL1);
+ isb();
+ write_sysreg(val, L2CPUSRDR_EL1);
+ isb();
+ raw_spin_unlock_irqrestore(&l2_access_lock, flags);
+}
+
+/**
+ * get_l2_indirect_reg: read an L2 register value
+ * @reg: Address of L2 register.
+ *
+ * Use architecturally required barriers for ordering between system register
+ * accesses
+ */
+static u64 get_l2_indirect_reg(u64 reg)
+{
+ u64 val;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&l2_access_lock, flags);
+ write_sysreg(reg, L2CPUSRSELR_EL1);
+ isb();
+ val = read_sysreg(L2CPUSRDR_EL1);
+ raw_spin_unlock_irqrestore(&l2_access_lock, flags);
+
+ return val;
+}
+
+/*
+ * Aggregate PMU. Implements the core pmu functions and manages
+ * the hardware PMUs.
+ */
+struct l2cache_pmu {
+ struct hlist_node node;
+ u32 num_pmus;
+ struct pmu pmu;
+ int num_counters;
+ cpumask_t cpumask;
+ struct platform_device *pdev;
+};
+
+/*
+ * The cache is made up of one or more clusters, each cluster has its own PMU.
+ * Each cluster is associated with one or more CPUs.
+ * This structure represents one of the hardware PMUs.
+ *
+ * Events can be envisioned as a 2-dimensional array. Each column represents
+ * a group of events. There are 8 groups. Only one entry from each
+ * group can be in use at a time. When an event is assigned a counter
+ * by *_event_add(), the counter index is assigned to group_to_counter[group].
+ * This allows *filter_match() to detect and reject conflicting events in
+ * the same group.
+ * Events are specified as 0xCCG, where CC is 2 hex digits specifying
+ * the code (array row) and G specifies the group (column).
+ *
+ * In addition there is a cycle counter event specified by L2CYCLE_CTR_RAW_CODE
+ * which is outside the above scheme.
+ */
+struct hml2_pmu {
+ struct perf_event *events[MAX_L2_CTRS];
+ struct l2cache_pmu *l2cache_pmu;
+ DECLARE_BITMAP(used_counters, MAX_L2_CTRS);
+ DECLARE_BITMAP(used_groups, L2_EVT_GROUP_MAX + 1);
+ int group_to_counter[L2_EVT_GROUP_MAX + 1];
+ int irq;
+ /* The CPU that is used for collecting events on this cluster */
+ int on_cpu;
+ /* All the CPUs associated with this cluster */
+ cpumask_t cluster_cpus;
+ spinlock_t pmu_lock;
+};
+
+#define to_l2cache_pmu(p) (container_of(p, struct l2cache_pmu, pmu))
+
+static DEFINE_PER_CPU(struct hml2_pmu *, pmu_cluster);
+static u32 l2_cycle_ctr_idx;
+static u32 l2_counter_present_mask;
+
+static inline u32 idx_to_reg_bit(u32 idx)
+{
+ if (idx == l2_cycle_ctr_idx)
+ return BIT(L2CYCLE_CTR_BIT);
+
+ return BIT(idx);
+}
+
+static inline struct hml2_pmu *get_hml2_pmu(int cpu)
+{
+ return per_cpu(pmu_cluster, cpu);
+}
+
+static void hml2_pmu__reset_on_cluster(void *x)
+{
+ /* Reset all ctrs */
+ set_l2_indirect_reg(L2PMCR, L2PMCR_RESET_ALL);
+ set_l2_indirect_reg(L2PMCNTENCLR, l2_counter_present_mask);
+ set_l2_indirect_reg(L2PMINTENCLR, l2_counter_present_mask);
+ set_l2_indirect_reg(L2PMOVSCLR, l2_counter_present_mask);
+}
+
+static inline void hml2_pmu__reset(struct hml2_pmu *cluster)
+{
+ cpumask_t *mask = &cluster->cluster_cpus;
+
+ if (smp_call_function_any(mask, hml2_pmu__reset_on_cluster, NULL, 1))
+ dev_err(&cluster->l2cache_pmu->pdev->dev,
+ "Failed to reset on cluster with cpu %d\n",
+ cpumask_first(&cluster->cluster_cpus));
+}
+
+static inline void hml2_pmu__enable(void)
+{
+ set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_ENABLE);
+}
+
+static inline void hml2_pmu__disable(void)
+{
+ set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_DISABLE);
+}
+
+static inline void hml2_pmu__counter_set_value(u32 idx, u64 value)
+{
+ u32 counter_reg;
+
+ if (idx == l2_cycle_ctr_idx) {
+ set_l2_indirect_reg(L2PMCCNTR, value);
+ } else {
+ counter_reg = (idx * IA_L2_REG_OFFSET) + IA_L2PMXEVCNTR_BASE;
+ set_l2_indirect_reg(counter_reg, value & GENMASK(31, 0));
+ }
+}
+
+static inline u64 hml2_pmu__counter_get_value(u32 idx)
+{
+ u64 value;
+ u32 counter_reg;
+
+ if (idx == l2_cycle_ctr_idx) {
+ value = get_l2_indirect_reg(L2PMCCNTR);
+ } else {
+ counter_reg = (idx * IA_L2_REG_OFFSET) + IA_L2PMXEVCNTR_BASE;
+ value = get_l2_indirect_reg(counter_reg);
+ }
+
+ return value;
+}
+
+static inline void hml2_pmu__counter_enable(u32 idx)
+{
+ set_l2_indirect_reg(L2PMCNTENSET, idx_to_reg_bit(idx));
+}
+
+static inline void hml2_pmu__counter_disable(u32 idx)
+{
+ set_l2_indirect_reg(L2PMCNTENCLR, idx_to_reg_bit(idx));
+}
+
+static inline void hml2_pmu__counter_enable_interrupt(u32 idx)
+{
+ set_l2_indirect_reg(L2PMINTENSET, idx_to_reg_bit(idx));
+}
+
+static inline void hml2_pmu__counter_disable_interrupt(u32 idx)
+{
+ set_l2_indirect_reg(L2PMINTENCLR, idx_to_reg_bit(idx));
+}
+
+static inline void hml2_pmu__set_evccntcr(u32 val)
+{
+ set_l2_indirect_reg(L2PMCCNTCR, val);
+}
+
+static inline void hml2_pmu__set_evcntcr(u32 ctr, u32 val)
+{
+ u32 evtcr_reg = (ctr * IA_L2_REG_OFFSET) + IA_L2PMXEVCNTCR_BASE;
+
+ set_l2_indirect_reg(evtcr_reg, val);
+}
+
+static inline void hml2_pmu__set_evtyper(u32 ctr, u32 val)
+{
+ u32 evtype_reg = (ctr * IA_L2_REG_OFFSET) + IA_L2PMXEVTYPER_BASE;
+
+ set_l2_indirect_reg(evtype_reg, val);
+}
+
+static void hml2_pmu__set_resr(struct hml2_pmu *cluster,
+ u32 event_group, u32 event_cc)
+{
+ u64 field;
+ u64 resr_val;
+ u32 shift;
+ unsigned long flags;
+
+ shift = L2PMRESR_GROUP_BITS * event_group;
+ field = ((u64)(event_cc & L2PMRESR_GROUP_MASK) << shift) | L2PMRESR_EN;
+
+ spin_lock_irqsave(&cluster->pmu_lock, flags);
+
+ resr_val = get_l2_indirect_reg(L2PMRESR);
+ resr_val &= ~(L2PMRESR_GROUP_MASK << shift);
+ resr_val |= field;
+ set_l2_indirect_reg(L2PMRESR, resr_val);
+
+ spin_unlock_irqrestore(&cluster->pmu_lock, flags);
+}
+
+/*
+ * Hardware allows filtering of events based on the originating
+ * CPU. Turn this off by setting filter bits to allow events from
+ * all CPUS, subunits and ID independent events in this cluster.
+ */
+static inline void hml2_pmu__set_evfilter_sys_mode(u32 ctr)
+{
+ u32 reg = (ctr * IA_L2_REG_OFFSET) + IA_L2PMXEVFILTER_BASE;
+ u32 val = L2PMXEVFILTER_SUFILTER_ALL |
+ L2PMXEVFILTER_ORGFILTER_IDINDEP |
+ L2PMXEVFILTER_ORGFILTER_ALL;
+
+ set_l2_indirect_reg(reg, val);
+}
+
+static inline u32 hml2_pmu__getreset_ovsr(void)
+{
+ u32 result = get_l2_indirect_reg(L2PMOVSSET);
+
+ set_l2_indirect_reg(L2PMOVSCLR, result);
+ return result;
+}
+
+static inline bool hml2_pmu__has_overflowed(u32 ovsr)
+{
+ return !!(ovsr & l2_counter_present_mask);
+}
+
+static inline bool hml2_pmu__counter_has_overflowed(u32 ovsr, u32 idx)
+{
+ return !!(ovsr & idx_to_reg_bit(idx));
+}
+
+static void l2_cache__event_update_from_cluster(struct perf_event *event,
+ struct hml2_pmu *cluster)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ u64 delta64, prev, now;
+ u32 delta;
+ u32 idx = hwc->idx;
+
+ do {
+ prev = local64_read(&hwc->prev_count);
+ now = hml2_pmu__counter_get_value(idx);
+ } while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev);
+
+ if (idx == l2_cycle_ctr_idx) {
+ /*
+ * The cycle counter is 64-bit so needs separate handling
+ * of 64-bit delta.
+ */
+ delta64 = now - prev;
+ local64_add(delta64, &event->count);
+ } else {
+ /*
+ * 32-bit counters need the unsigned 32-bit math to handle
+ * overflow and now < prev
+ */
+ delta = now - prev;
+ local64_add(delta, &event->count);
+ }
+}
+
+static void l2_cache__cluster_set_period(struct hml2_pmu *cluster,
+ struct hw_perf_event *hwc)
+{
+ u64 base = L2_MAX_PERIOD - (L2_CNT_PERIOD - 1);
+ u32 idx = hwc->idx;
+ u64 prev = local64_read(&hwc->prev_count);
+ u64 value;
+
+ /*
+ * Limit the maximum period to prevent the counter value
+ * from overtaking the one we are about to program.
+ * Use a starting value which is high enough that after
+ * an overflow, interrupt latency will not cause the count
+ * to reach the base value. If the previous value
+ * is below the base, increase it to be above the base
+ * and update prev_count accordingly. Otherwise if
+ * the previous value is already above the base
+ * nothing needs to be done to prev_count.
+ */
+ if (prev < base) {
+ value = base + prev;
+ local64_set(&hwc->prev_count, value);
+ } else {
+ value = prev;
+ }
+
+ hml2_pmu__counter_set_value(idx, value);
+}
+
+static int l2_cache__get_event_idx(struct hml2_pmu *cluster,
+ struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx;
+
+ if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
+ if (test_and_set_bit(l2_cycle_ctr_idx, cluster->used_counters))
+ return -EAGAIN;
+
+ return l2_cycle_ctr_idx;
+ }
+
+ for (idx = 0; idx < cluster->l2cache_pmu->num_counters - 1; idx++) {
+ if (!test_and_set_bit(idx, cluster->used_counters)) {
+ set_bit(L2_EVT_GROUP(hwc->config_base),
+ cluster->used_groups);
+ return idx;
+ }
+ }
+
+ /* The counters are all in use. */
+ return -EAGAIN;
+}
+
+static void l2_cache__clear_event_idx(struct hml2_pmu *cluster,
+ struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ clear_bit(idx, cluster->used_counters);
+ if (hwc->config_base != L2CYCLE_CTR_RAW_CODE)
+ clear_bit(L2_EVT_GROUP(hwc->config_base), cluster->used_groups);
+}
+
+static irqreturn_t l2_cache__handle_irq(int irq_num, void *data)
+{
+ struct hml2_pmu *cluster = data;
+ int num_counters = cluster->l2cache_pmu->num_counters;
+ u32 ovsr;
+ int idx;
+
+ ovsr = hml2_pmu__getreset_ovsr();
+ if (!hml2_pmu__has_overflowed(ovsr))
+ return IRQ_NONE;
+
+ for_each_set_bit(idx, cluster->used_counters, num_counters) {
+ struct perf_event *event = cluster->events[idx];
+ struct hw_perf_event *hwc;
+
+ if (!hml2_pmu__counter_has_overflowed(ovsr, idx))
+ continue;
+
+ l2_cache__event_update_from_cluster(event, cluster);
+ hwc = &event->hw;
+
+ l2_cache__cluster_set_period(cluster, hwc);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Implementation of abstract pmu functionality required by
+ * the core perf events code.
+ */
+
+static void l2_cache__pmu_enable(struct pmu *pmu)
+{
+ /*
+ * Although there is only one PMU (per socket) controlling multiple
+ * physical PMUs (per cluster), because we do not support per-task mode
+ * each event is associated with a CPU. Each event has pmu_enable
+ * called on its CPU, so here it is only necessary to enable the
+ * counters for the current CPU.
+ */
+
+ hml2_pmu__enable();
+}
+
+static void l2_cache__pmu_disable(struct pmu *pmu)
+{
+ hml2_pmu__disable();
+}
+
+static int l2_cache__event_init(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hml2_pmu *cluster;
+ struct perf_event *sibling;
+ struct l2cache_pmu *l2cache_pmu;
+
+ if (event->attr.type != event->pmu->type)
+ return -ENOENT;
+
+ l2cache_pmu = to_l2cache_pmu(event->pmu);
+
+ if (hwc->sample_period) {
+ dev_warn(&l2cache_pmu->pdev->dev, "Sampling not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (event->cpu < 0) {
+ dev_warn(&l2cache_pmu->pdev->dev, "Per-task mode not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* We cannot filter accurately so we just don't allow it. */
+ if (event->attr.exclude_user || event->attr.exclude_kernel ||
+ event->attr.exclude_hv || event->attr.exclude_idle) {
+ dev_warn(&l2cache_pmu->pdev->dev, "Can't exclude execution levels\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (((L2_EVT_GROUP(event->attr.config) > L2_EVT_GROUP_MAX) ||
+ ((event->attr.config & ~L2_EVT_MASK) != 0)) &&
+ (event->attr.config != L2CYCLE_CTR_RAW_CODE)) {
+ dev_warn(&l2cache_pmu->pdev->dev, "Invalid config %llx\n",
+ event->attr.config);
+ return -EINVAL;
+ }
+
+ /* Don't allow groups with mixed PMUs, except for s/w events */
+ if (event->group_leader->pmu != event->pmu &&
+ !is_software_event(event->group_leader)) {
+ dev_warn(&l2cache_pmu->pdev->dev,
+ "Can't create mixed PMU group\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(sibling, &event->group_leader->sibling_list,
+ group_entry)
+ if (sibling->pmu != event->pmu &&
+ !is_software_event(sibling)) {
+ dev_warn(&l2cache_pmu->pdev->dev,
+ "Can't create mixed PMU group\n");
+ return -EINVAL;
+ }
+
+ /* Ensure all events in a group are on the same cpu */
+ cluster = get_hml2_pmu(event->cpu);
+ if ((event->group_leader != event) &&
+ (cluster->on_cpu != event->group_leader->cpu)) {
+ dev_warn(&l2cache_pmu->pdev->dev,
+ "Can't create group on CPUs %d and %d",
+ event->cpu, event->group_leader->cpu);
+ return -EINVAL;
+ }
+
+ hwc->idx = -1;
+ hwc->config_base = event->attr.config;
+
+ /*
+ * Ensure all events are on the same cpu so all events are in the
+ * same cpu context, to avoid races on pmu_enable etc.
+ */
+ event->cpu = cluster->on_cpu;
+
+ return 0;
+}
+
+static void l2_cache__event_start(struct perf_event *event, int flags)
+{
+ struct hml2_pmu *cluster;
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+ u32 config;
+ u32 event_cc, event_group;
+
+ hwc->state = 0;
+
+ cluster = get_hml2_pmu(event->cpu);
+ l2_cache__cluster_set_period(cluster, hwc);
+
+ if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
+ hml2_pmu__set_evccntcr(0x0);
+ } else {
+ config = hwc->config_base;
+ event_cc = L2_EVT_CODE(config);
+ event_group = L2_EVT_GROUP(config);
+
+ hml2_pmu__set_evcntcr(idx, 0x0);
+ hml2_pmu__set_evtyper(idx, event_group);
+ hml2_pmu__set_resr(cluster, event_group, event_cc);
+ hml2_pmu__set_evfilter_sys_mode(idx);
+ }
+
+ hml2_pmu__counter_enable_interrupt(idx);
+ hml2_pmu__counter_enable(idx);
+}
+
+static void l2_cache__event_stop(struct perf_event *event, int flags)
+{
+ struct hml2_pmu *cluster;
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ if (!(hwc->state & PERF_HES_STOPPED)) {
+ cluster = get_hml2_pmu(event->cpu);
+ hml2_pmu__counter_disable_interrupt(idx);
+ hml2_pmu__counter_disable(idx);
+
+ if (flags & PERF_EF_UPDATE)
+ l2_cache__event_update_from_cluster(event, cluster);
+ hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
+ }
+}
+
+static int l2_cache__event_add(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx;
+ int err = 0;
+ struct hml2_pmu *cluster;
+
+ cluster = get_hml2_pmu(event->cpu);
+
+ idx = l2_cache__get_event_idx(cluster, event);
+ if (idx < 0) {
+ err = idx;
+ return err;
+ }
+
+ hwc->idx = idx;
+ hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+ cluster->events[idx] = event;
+ cluster->group_to_counter[L2_EVT_GROUP(hwc->config_base)] = idx;
+ local64_set(&hwc->prev_count, 0ULL);
+
+ if (flags & PERF_EF_START)
+ l2_cache__event_start(event, flags);
+
+ /* Propagate changes to the userspace mapping. */
+ perf_event_update_userpage(event);
+
+ return err;
+}
+
+static void l2_cache__event_del(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hml2_pmu *cluster;
+ int idx = hwc->idx;
+
+ cluster = get_hml2_pmu(event->cpu);
+ l2_cache__event_stop(event, flags | PERF_EF_UPDATE);
+ cluster->events[idx] = NULL;
+ l2_cache__clear_event_idx(cluster, event);
+
+ perf_event_update_userpage(event);
+}
+
+static void l2_cache__event_read(struct perf_event *event)
+{
+ l2_cache__event_update_from_cluster(event, get_hml2_pmu(event->cpu));
+}
+
+static int l2_cache_filter_match(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hml2_pmu *cluster = get_hml2_pmu(event->cpu);
+ unsigned int group = L2_EVT_GROUP(hwc->config_base);
+
+ /* check for column exclusion: group already in use by another event */
+ if (test_bit(group, cluster->used_groups) &&
+ cluster->events[cluster->group_to_counter[group]] != event)
+ return 0;
+
+ return 1;
+}
+
+static ssize_t l2_cache_pmu_cpumask_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct l2cache_pmu *l2cache_pmu = to_l2cache_pmu(dev_get_drvdata(dev));
+
+ return cpumap_print_to_pagebuf(true, buf, &l2cache_pmu->cpumask);
+}
+
+static struct device_attribute l2_cache_pmu_cpumask_attr =
+ __ATTR(cpumask, S_IRUGO, l2_cache_pmu_cpumask_show, NULL);
+
+static struct attribute *l2_cache_pmu_cpumask_attrs[] = {
+ &l2_cache_pmu_cpumask_attr.attr,
+ NULL,
+};
+
+static struct attribute_group l2_cache_pmu_cpumask_group = {
+ .attrs = l2_cache_pmu_cpumask_attrs,
+};
+
+/* CCG format for perf RAW codes. */
+PMU_FORMAT_ATTR(l2_code, "config:4-11");
+PMU_FORMAT_ATTR(l2_group, "config:0-3");
+static struct attribute *l2_cache_pmu_formats[] = {
+ &format_attr_l2_code.attr,
+ &format_attr_l2_group.attr,
+ NULL,
+};
+
+static struct attribute_group l2_cache_pmu_format_group = {
+ .name = "format",
+ .attrs = l2_cache_pmu_formats,
+};
+
+static const struct attribute_group *l2_cache_pmu_attr_grps[] = {
+ &l2_cache_pmu_format_group,
+ &l2_cache_pmu_cpumask_group,
+ NULL,
+};
+
+/*
+ * Generic device handlers
+ */
+
+static const struct acpi_device_id l2_cache_pmu_acpi_match[] = {
+ { "QCOM8130", },
+ { }
+};
+
+static int get_num_counters(void)
+{
+ int val;
+
+ val = get_l2_indirect_reg(L2PMCR);
+
+ /*
+ * Read number of counters from L2PMCR and add 1
+ * for the cycle counter.
+ */
+ return ((val >> L2PMCR_NUM_EV_SHIFT) & L2PMCR_NUM_EV_MASK) + 1;
+}
+
+static int l2cache_pmu_online_cpu(unsigned int cpu, struct hlist_node *node)
+{
+ struct hml2_pmu *cluster;
+ cpumask_t cluster_online_cpus;
+ struct l2cache_pmu *l2cache_pmu;
+
+ l2cache_pmu = hlist_entry_safe(node, struct l2cache_pmu, node);
+ cluster = get_hml2_pmu(cpu);
+ cpumask_and(&cluster_online_cpus, &cluster->cluster_cpus,
+ cpu_online_mask);
+
+ if (cpumask_weight(&cluster_online_cpus) == 1) {
+ /* all CPUs on this cluster were down, use this one */
+ cluster->on_cpu = cpu;
+ cpumask_set_cpu(cpu, &l2cache_pmu->cpumask);
+ WARN_ON(irq_set_affinity(cluster->irq, cpumask_of(cpu)));
+ }
+
+ return 0;
+}
+
+static int l2cache_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
+{
+ struct hml2_pmu *cluster;
+ struct l2cache_pmu *l2cache_pmu;
+ cpumask_t cluster_online_cpus;
+ unsigned int target;
+
+ l2cache_pmu = hlist_entry_safe(node, struct l2cache_pmu, node);
+
+ if (!cpumask_test_and_clear_cpu(cpu, &l2cache_pmu->cpumask))
+ return 0;
+ cluster = get_hml2_pmu(cpu);
+ cpumask_and(&cluster_online_cpus, &cluster->cluster_cpus,
+ cpu_online_mask);
+
+ /* Any other CPU for this cluster which is still online */
+ target = cpumask_any_but(&cluster_online_cpus, cpu);
+ if (target >= nr_cpu_ids)
+ return 0;
+
+ perf_pmu_migrate_context(&l2cache_pmu->pmu, cpu, target);
+ cluster->on_cpu = target;
+ cpumask_set_cpu(target, &l2cache_pmu->cpumask);
+ WARN_ON(irq_set_affinity(cluster->irq, cpumask_of(target)));
+
+ return 0;
+}
+
+static int l2_cache_pmu_probe_cluster(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev->parent);
+ struct platform_device *sdev = to_platform_device(dev);
+ struct l2cache_pmu *l2cache_pmu = data;
+ struct hml2_pmu *cluster;
+ struct acpi_device *device;
+ unsigned long fw_cluster_id;
+ int cpu;
+ int err;
+ int irq;
+
+ if (acpi_bus_get_device(ACPI_HANDLE(dev), &device))
+ return -ENODEV;
+
+ if (kstrtol(device->pnp.unique_id, 10, &fw_cluster_id) < 0) {
+ dev_err(&pdev->dev, "unable to read ACPI uid\n");
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq(sdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev,
+ "Failed to get valid irq for cluster %ld\n",
+ fw_cluster_id);
+ return irq;
+ }
+
+ cluster = devm_kzalloc(&pdev->dev, sizeof(*cluster), GFP_KERNEL);
+ if (!cluster)
+ return -ENOMEM;
+
+ cluster->l2cache_pmu = l2cache_pmu;
+ for_each_present_cpu(cpu) {
+ if (topology_physical_package_id(cpu) == fw_cluster_id) {
+ cpumask_set_cpu(cpu, &cluster->cluster_cpus);
+ per_cpu(pmu_cluster, cpu) = cluster;
+ }
+ }
+ cluster->irq = irq;
+
+ if (cpumask_empty(&cluster->cluster_cpus)) {
+ dev_err(&pdev->dev, "No CPUs found for L2 cache instance %ld\n",
+ fw_cluster_id);
+ return -ENODEV;
+ }
+
+ /* Pick one CPU to be the preferred one to use in the cluster */
+ cluster->on_cpu = cpumask_first(&cluster->cluster_cpus);
+
+ if (irq_set_affinity(irq, cpumask_of(cluster->on_cpu))) {
+ dev_err(&pdev->dev,
+ "Unable to set irq affinity (irq=%d, cpu=%d)\n",
+ irq, cluster->on_cpu);
+ return -ENODEV;
+ }
+
+ err = devm_request_irq(&pdev->dev, irq, l2_cache__handle_irq,
+ IRQF_NOBALANCING, "l2-cache-pmu", cluster);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Unable to request IRQ%d for L2 PMU counters\n", irq);
+ return err;
+ }
+
+ dev_info(&pdev->dev,
+ "Registered L2 cache PMU instance %ld with %d CPUs\n",
+ fw_cluster_id, cpumask_weight(&cluster->cluster_cpus));
+
+ cluster->pmu_lock = __SPIN_LOCK_UNLOCKED(cluster->pmu_lock);
+ cpumask_set_cpu(cluster->on_cpu, &l2cache_pmu->cpumask);
+
+ hml2_pmu__reset(cluster);
+ l2cache_pmu->num_pmus++;
+
+ return 0;
+}
+
+static int l2_cache_pmu_probe(struct platform_device *pdev)
+{
+ int err;
+ struct l2cache_pmu *l2cache_pmu;
+
+ l2cache_pmu =
+ devm_kzalloc(&pdev->dev, sizeof(*l2cache_pmu), GFP_KERNEL);
+ if (!l2cache_pmu)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, l2cache_pmu);
+ l2cache_pmu->pmu = (struct pmu) {
+ /* suffix is instance id for future use with multiple sockets */
+ .name = "l2cache_0",
+ .task_ctx_nr = perf_invalid_context,
+ .pmu_enable = l2_cache__pmu_enable,
+ .pmu_disable = l2_cache__pmu_disable,
+ .event_init = l2_cache__event_init,
+ .add = l2_cache__event_add,
+ .del = l2_cache__event_del,
+ .start = l2_cache__event_start,
+ .stop = l2_cache__event_stop,
+ .read = l2_cache__event_read,
+ .attr_groups = l2_cache_pmu_attr_grps,
+ .filter_match = l2_cache_filter_match,
+ };
+
+ l2cache_pmu->num_counters = get_num_counters();
+ l2cache_pmu->pdev = pdev;
+ l2_cycle_ctr_idx = l2cache_pmu->num_counters - 1;
+ l2_counter_present_mask = GENMASK(l2cache_pmu->num_counters - 2, 0) |
+ L2PM_CC_ENABLE;
+
+ cpumask_clear(&l2cache_pmu->cpumask);
+
+ /* Read cluster info and initialize each cluster */
+ err = device_for_each_child(&pdev->dev, l2cache_pmu,
+ l2_cache_pmu_probe_cluster);
+ if (err < 0)
+ return err;
+
+ if (l2cache_pmu->num_pmus == 0) {
+ dev_err(&pdev->dev, "No hardware L2 cache PMUs found\n");
+ return -ENODEV;
+ }
+
+ err = perf_pmu_register(&l2cache_pmu->pmu, l2cache_pmu->pmu.name, -1);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Error %d registering L2 cache PMU\n", err);
+ return err;
+ }
+
+ dev_info(&pdev->dev, "Registered L2 cache PMU using %d HW PMUs\n",
+ l2cache_pmu->num_pmus);
+
+ err = cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
+ &l2cache_pmu->node);
+
+ return err;
+}
+
+static int l2_cache_pmu_remove(struct platform_device *pdev)
+{
+ struct l2cache_pmu *l2cache_pmu =
+ to_l2cache_pmu(platform_get_drvdata(pdev));
+
+ cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
+ &l2cache_pmu->node);
+ perf_pmu_unregister(&l2cache_pmu->pmu);
+ return 0;
+}
+
+static struct platform_driver l2_cache_pmu_driver = {
+ .driver = {
+ .name = "qcom-l2cache-pmu",
+ .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(l2_cache_pmu_acpi_match),
+ },
+ .probe = l2_cache_pmu_probe,
+ .remove = l2_cache_pmu_remove,
+};
+
+static int __init register_l2_cache_pmu_driver(void)
+{
+ int err;
+
+ err = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
+ "AP_PERF_ARM_QCOM_L2_ONLINE",
+ l2cache_pmu_online_cpu,
+ l2cache_pmu_offline_cpu);
+ if (err)
+ return err;
+
+ return platform_driver_register(&l2_cache_pmu_driver);
+}
+device_initcall(register_l2_cache_pmu_driver);
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 5dbb26e..eb73c21 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -102,6 +102,7 @@ enum cpuhp_state {
CPUHP_AP_PERF_ARM_CCI_ONLINE,
CPUHP_AP_PERF_ARM_CCN_ONLINE,
CPUHP_AP_PERF_ARM_L2X0_ONLINE,
+ CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
CPUHP_AP_WORKQUEUE_ONLINE,
CPUHP_AP_RCUTREE_ONLINE,
CPUHP_AP_NOTIFY_ONLINE,
--
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.
^ permalink raw reply related
* [PATCH -next] phy: rockchip-typec: fix non static symbol warnings
From: Wei Yongjun @ 2016-09-21 15:05 UTC (permalink / raw)
To: linux-arm-kernel
From: Wei Yongjun <weiyongjun1@huawei.com>
Fixes the following sparse warnings:
drivers/phy/phy-rockchip-typec.c:295:16: warning:
symbol 'usb3_pll_cfg' was not declared. Should it be static?
drivers/phy/phy-rockchip-typec.c:312:16: warning:
symbol 'dp_pll_cfg' was not declared. Should it be static?
Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
---
drivers/phy/phy-rockchip-typec.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/phy-rockchip-typec.c b/drivers/phy/phy-rockchip-typec.c
index 7cfb0f8..555604c 100644
--- a/drivers/phy/phy-rockchip-typec.c
+++ b/drivers/phy/phy-rockchip-typec.c
@@ -292,7 +292,7 @@ struct phy_reg {
u32 addr;
};
-struct phy_reg usb3_pll_cfg[] = {
+static struct phy_reg usb3_pll_cfg[] = {
{ 0xf0, CMN_PLL0_VCOCAL_INIT },
{ 0x18, CMN_PLL0_VCOCAL_ITER },
{ 0xd0, CMN_PLL0_INTDIV },
@@ -309,7 +309,7 @@ struct phy_reg usb3_pll_cfg[] = {
{ 0x8, CMN_DIAG_PLL0_LF_PROG },
};
-struct phy_reg dp_pll_cfg[] = {
+static struct phy_reg dp_pll_cfg[] = {
{ 0xf0, CMN_PLL1_VCOCAL_INIT },
{ 0x18, CMN_PLL1_VCOCAL_ITER },
{ 0x30b9, CMN_PLL1_VCOCAL_START },
^ permalink raw reply related
* [PATCH v2] drivers: psci: PSCI checker module
From: Kevin Brodsky @ 2016-09-21 14:39 UTC (permalink / raw)
To: linux-arm-kernel
On arm and arm64, PSCI is one of the possible firmware interfaces
used for power management. This includes both turning CPUs on and off,
and suspending them (entering idle states).
This patch adds a PSCI checker module that enables basic testing of
PSCI operations during startup. There are two main tests: CPU
hotplugging and suspending.
In the hotplug tests, the hotplug API is used to turn off and on again
all CPUs in the system, and then all CPUs in each cluster, checking
the consistency of the return codes.
In the suspend tests, a high-priority thread is created on each core
and uses low-level cpuidle functionalities to enter suspend, in all
the possible states and multiple times. This should allow a maximum
number of CPUs to enter the same sleep state at the same or slightly
different time.
In essence, the suspend tests use a principle similar to that of the
intel_powerclamp driver (drivers/thermal/intel_powerclamp.c), but the
threads are only kept for the duration of the test (they are already
gone when userspace is started).
While in theory power management PSCI functions (CPU_{ON,OFF,SUSPEND})
could be directly called, this proved too difficult as it would imply
the duplication of all the logic used by the kernel to allow for a
clean shutdown/bringup/suspend of the CPU (the deepest sleep states
implying potentially the shutdown of the CPU).
Note that this file cannot be compiled as a loadable module, since it
uses a number of non-exported identifiers (essentially for
PSCI-specific checks and direct use of cpuidle) and relies on the
absence of userspace to avoid races when calling hotplug and cpuidle
functions.
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Kevin Hilman <khilman@kernel.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Kevin Brodsky <kevin.brodsky@arm.com>
---
Changelog v1..v2:
* Do not count tick_broadcast_enter() failures as errors, as they may
be unavoidable. When it happens, fall back to WFI.
* Do not count unexpected sleep states as errors (currently, the only
case is when falling back to WFI). Instead, report the number of
times it happens before the suspend thread exits.
* Use usecs_to_jiffies() to compute the suspend timeout. The previous
version resulted in a zero timeout if the target residency was
shorter than a jiffy.
* Various cleanup.
Thanks to Lorenzo for his help with improving this patch!
Kevin
drivers/firmware/Kconfig | 7 +
drivers/firmware/Makefile | 1 +
drivers/firmware/psci_checker.c | 488 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 496 insertions(+)
create mode 100644 drivers/firmware/psci_checker.c
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 0e22f241403b..0033c8755104 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -206,6 +206,13 @@ config QCOM_SCM_64
config HAVE_ARM_SMCCC
bool
+config PSCI_CHECKER
+ bool "PSCI checker"
+ depends on ARM_PSCI_FW && HOTPLUG_CPU
+ help
+ Run the PSCI checker during startup. This checks that hotplug and
+ suspend operations work correctly when using PSCI.
+
source "drivers/firmware/broadcom/Kconfig"
source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig"
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 44a59dcfc398..3efc794906c6 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_QCOM_SCM) += qcom_scm.o
obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o
obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o
CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
+obj-$(CONFIG_PSCI_CHECKER) += psci_checker.o
obj-y += broadcom/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
diff --git a/drivers/firmware/psci_checker.c b/drivers/firmware/psci_checker.c
new file mode 100644
index 000000000000..c8992c0be630
--- /dev/null
+++ b/drivers/firmware/psci_checker.c
@@ -0,0 +1,488 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2016 ARM Limited
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/atomic.h>
+#include <linux/completion.h>
+#include <linux/cpu.h>
+#include <linux/cpuidle.h>
+#include <linux/cpu_pm.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/preempt.h>
+#include <linux/psci.h>
+#include <linux/slab.h>
+#include <linux/tick.h>
+#include <linux/topology.h>
+
+#include <asm/cpuidle.h>
+
+#include <uapi/linux/psci.h>
+
+#define NUM_SUSPEND_CYCLE (10)
+
+static unsigned int nb_available_cpus;
+static int tos_resident_cpu = -1;
+
+static atomic_t nb_active_threads;
+static struct completion suspend_threads_started =
+ COMPLETION_INITIALIZER(suspend_threads_started);
+static struct completion suspend_threads_done =
+ COMPLETION_INITIALIZER(suspend_threads_done);
+
+/*
+ * We assume that PSCI operations are used if they are available. This is not
+ * necessarily true on arm64, since the decision is based on the
+ * "enable-method" property of each CPU in the DT, but given that there is no
+ * arch-specific way to check this, we assume that the DT is sensible.
+ */
+static int psci_ops_check(void)
+{
+ int migrate_type = -1;
+ int cpu;
+
+ if (!(psci_ops.cpu_off && psci_ops.cpu_on && psci_ops.cpu_suspend)) {
+ pr_warn("Missing PSCI operations, aborting tests\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (psci_ops.migrate_info_type)
+ migrate_type = psci_ops.migrate_info_type();
+
+ if (migrate_type == PSCI_0_2_TOS_UP_MIGRATE ||
+ migrate_type == PSCI_0_2_TOS_UP_NO_MIGRATE) {
+ /* There is a UP Trusted OS, find on which core it resides. */
+ for_each_online_cpu(cpu)
+ if (psci_tos_resident_on(cpu)) {
+ tos_resident_cpu = cpu;
+ break;
+ }
+ if (tos_resident_cpu == -1)
+ pr_warn("UP Trusted OS resides on no online CPU\n");
+ }
+
+ return 0;
+}
+
+static int find_clusters(const struct cpumask *cpus,
+ const struct cpumask **clusters)
+{
+ unsigned int nb = 0;
+ cpumask_var_t tmp;
+
+ if (!alloc_cpumask_var(&tmp, GFP_KERNEL))
+ return -ENOMEM;
+ cpumask_copy(tmp, cpus);
+
+ while (!cpumask_empty(tmp)) {
+ const struct cpumask *cluster =
+ topology_core_cpumask(cpumask_any(tmp));
+
+ clusters[nb++] = cluster;
+ cpumask_andnot(tmp, tmp, cluster);
+ }
+
+ free_cpumask_var(tmp);
+ return nb;
+}
+
+/*
+ * offlined_cpus is a temporary array but passing it as an argument avoids
+ * multiple allocations.
+ */
+static unsigned int down_and_up_cpus(const struct cpumask *cpus,
+ struct cpumask *offlined_cpus)
+{
+ int cpu;
+ int err = 0;
+
+ cpumask_clear(offlined_cpus);
+
+ /* Try to power down all CPUs in the mask. */
+ for_each_cpu(cpu, cpus) {
+ int ret = cpu_down(cpu);
+
+ /*
+ * cpu_down() checks the number of online CPUs before the TOS
+ * resident CPU.
+ */
+ if (cpumask_weight(offlined_cpus) + 1 == nb_available_cpus) {
+ if (ret != -EBUSY) {
+ pr_err("Unexpected return code %d while trying "
+ "to power down last online CPU %d\n",
+ ret, cpu);
+ ++err;
+ }
+ } else if (cpu == tos_resident_cpu) {
+ if (ret != -EPERM) {
+ pr_err("Unexpected return code %d while trying "
+ "to power down TOS resident CPU %d\n",
+ ret, cpu);
+ ++err;
+ }
+ } else if (ret != 0) {
+ pr_err("Error occurred (%d) while trying "
+ "to power down CPU %d\n", ret, cpu);
+ ++err;
+ }
+
+ if (ret == 0)
+ cpumask_set_cpu(cpu, offlined_cpus);
+ }
+
+ /* Try to power up all the CPUs that have been offlined. */
+ for_each_cpu(cpu, offlined_cpus) {
+ int ret = cpu_up(cpu);
+
+ if (ret != 0) {
+ pr_err("Error occurred (%d) while trying "
+ "to power up CPU %d\n", ret, cpu);
+ ++err;
+ } else {
+ cpumask_clear_cpu(cpu, offlined_cpus);
+ }
+ }
+
+ /*
+ * Something went bad at some point and some CPUs could not be turned
+ * back on.
+ */
+ WARN_ON(!cpumask_empty(offlined_cpus) ||
+ num_online_cpus() != nb_available_cpus);
+
+ return err;
+}
+
+static int hotplug_tests(void)
+{
+ int err;
+ cpumask_var_t offlined_cpus;
+ int i, nb_cluster;
+ const struct cpumask **clusters;
+ char *page_buf;
+
+ err = -ENOMEM;
+ if (!alloc_cpumask_var(&offlined_cpus, GFP_KERNEL))
+ return err;
+ /* We may have up to nb_available_cpus clusters. */
+ clusters = kmalloc_array(nb_available_cpus, sizeof(*clusters),
+ GFP_KERNEL);
+ if (!clusters)
+ goto out_free_cpus;
+ page_buf = (char *)__get_free_page(GFP_KERNEL);
+ if (!page_buf)
+ goto out_free_clusters;
+
+ err = 0;
+ nb_cluster = find_clusters(cpu_online_mask, clusters);
+
+ /*
+ * Of course the last CPU cannot be powered down and cpu_down() should
+ * refuse doing that.
+ */
+ pr_info("Trying to turn off and on again all CPUs\n");
+ err += down_and_up_cpus(cpu_online_mask, offlined_cpus);
+
+ /*
+ * Take down CPUs by cluster this time. When the last CPU is turned
+ * off, the cluster itself should shut down.
+ */
+ for (i = 0; i < nb_cluster; ++i) {
+ int cluster_id =
+ topology_physical_package_id(cpumask_any(clusters[i]));
+ ssize_t len = cpumap_print_to_pagebuf(true, page_buf,
+ clusters[i]);
+ /* Remove trailing newline. */
+ page_buf[len - 1] = '\0';
+ pr_info("Trying to turn off and on again cluster %d "
+ "(CPUs %s)\n", cluster_id, page_buf);
+ err += down_and_up_cpus(clusters[i], offlined_cpus);
+ }
+
+ free_page((unsigned long)page_buf);
+out_free_clusters:
+ kfree(clusters);
+out_free_cpus:
+ free_cpumask_var(offlined_cpus);
+ return err;
+}
+
+static void dummy_callback(unsigned long ignored) {}
+
+static int suspend_cpu(int index, bool broadcast)
+{
+ int ret;
+
+ arch_cpu_idle_enter();
+
+ if (broadcast) {
+ /*
+ * The local timer will be shut down, we need to enter tick
+ * broadcast.
+ */
+ ret = tick_broadcast_enter();
+ if (ret) {
+ /*
+ * In the absence of hardware broadcast mechanism,
+ * this CPU might be used to broadcast wakeups, which
+ * may be why entering tick broadcast has failed.
+ * There is little the kernel can do to work around
+ * that, so enter WFI instead (idle state 0).
+ */
+ cpu_do_idle();
+ ret = 0;
+ goto out_arch_exit;
+ }
+ }
+
+ /*
+ * Replicate the common ARM cpuidle enter function
+ * (arm_enter_idle_state).
+ */
+ ret = CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, index);
+
+ if (broadcast)
+ tick_broadcast_exit();
+
+out_arch_exit:
+ arch_cpu_idle_exit();
+
+ return ret;
+}
+
+static int suspend_stress_thread(void *arg)
+{
+ int cpu = (long)arg;
+ int i, nb_suspend = 0, nb_shallow_sleep = 0, nb_err = 0;
+ struct sched_param sched_priority = { .sched_priority = MAX_RT_PRIO-1 };
+ struct cpuidle_device *dev;
+ struct cpuidle_driver *drv;
+ /* No need for an actual callback, we just want to wake up the CPU. */
+ struct timer_list wakeup_timer =
+ TIMER_INITIALIZER(dummy_callback, 0, 0);
+
+ /* Wait for the main thread to give the start signal. */
+ wait_for_completion(&suspend_threads_started);
+
+ /* Set maximum priority to preempt all other threads on this CPU. */
+ if (sched_setscheduler_nocheck(current, SCHED_FIFO, &sched_priority))
+ pr_warn("Failed to set suspend thread scheduler on CPU %d\n",
+ cpu);
+
+ dev = this_cpu_read(cpuidle_devices);
+ drv = cpuidle_get_cpu_driver(dev);
+
+ pr_info("CPU %d entering suspend cycles, states 1 through %d\n",
+ cpu, drv->state_count - 1);
+
+ for (i = 0; i < NUM_SUSPEND_CYCLE; ++i) {
+ int index;
+ /*
+ * Test all possible states, except 0 (which is usually WFI and
+ * doesn't use PSCI).
+ */
+ for (index = 1; index < drv->state_count; ++index) {
+ struct cpuidle_state *state = &drv->states[index];
+ bool broadcast = state->flags & CPUIDLE_FLAG_TIMER_STOP;
+ int ret;
+
+ /*
+ * Set the timer to wake this CPU up in some time (which
+ * should be largely sufficient for entering suspend).
+ * If the local tick is disabled when entering suspend,
+ * suspend_cpu() takes care of switching to a broadcast
+ * tick, so the timer will still wake us up.
+ */
+ mod_timer(&wakeup_timer, jiffies +
+ usecs_to_jiffies(state->target_residency));
+
+ /* IRQs must be disabled during suspend operations. */
+ local_irq_disable();
+
+ ret = suspend_cpu(index, broadcast);
+
+ /*
+ * We have woken up. Re-enable IRQs to handle any
+ * pending interrupt, do not wait until the end of the
+ * loop.
+ */
+ local_irq_enable();
+
+ if (ret == index) {
+ ++nb_suspend;
+ } else if (ret >= 0) {
+ /* We did not enter the expected state. */
+ ++nb_shallow_sleep;
+ } else {
+ pr_err("Failed to suspend CPU %d: error %d "
+ "(requested state %d, cycle %d)\n",
+ cpu, ret, index, i);
+ ++nb_err;
+ }
+ }
+ }
+
+ /*
+ * Disable the timer to make sure that the timer will not trigger
+ * later.
+ */
+ del_timer(&wakeup_timer);
+
+ if (atomic_dec_return_relaxed(&nb_active_threads) == 0)
+ complete(&suspend_threads_done);
+
+ /* Give up on RT scheduling and wait for termination. */
+ sched_priority.sched_priority = 0;
+ if (sched_setscheduler_nocheck(current, SCHED_NORMAL, &sched_priority))
+ pr_warn("Failed to set suspend thread scheduler on CPU %d\n",
+ cpu);
+ for (;;) {
+ /* Needs to be set first to avoid missing a wakeup. */
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (kthread_should_stop()) {
+ __set_current_state(TASK_RUNNING);
+ break;
+ }
+ schedule();
+ }
+
+ pr_info("CPU %d suspend test results: success %d, shallow states %d, errors %d\n",
+ cpu, nb_suspend, nb_shallow_sleep, nb_err);
+
+ return nb_err;
+}
+
+static int suspend_tests(void)
+{
+ int i, cpu, err = 0;
+ struct task_struct **threads;
+ int nb_threads = 0;
+
+ threads = kmalloc_array(nb_available_cpus, sizeof(*threads),
+ GFP_KERNEL);
+ if (!threads)
+ return -ENOMEM;
+
+ for_each_online_cpu(cpu) {
+ struct task_struct *thread;
+ /* Check that cpuidle is available on that CPU. */
+ struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
+ struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
+
+ if (cpuidle_not_available(drv, dev)) {
+ pr_warn("cpuidle not available on CPU %d, ignoring\n",
+ cpu);
+ continue;
+ }
+
+ thread = kthread_create_on_cpu(suspend_stress_thread,
+ (void *)(long)cpu, cpu,
+ "psci_suspend_stress");
+ if (IS_ERR(thread))
+ pr_err("Failed to create kthread on CPU %d\n", cpu);
+ else
+ threads[nb_threads++] = thread;
+ }
+ if (nb_threads < 1) {
+ kfree(threads);
+ return -ENODEV;
+ }
+
+ atomic_set(&nb_active_threads, nb_threads);
+
+ /*
+ * Stop cpuidle to prevent the idle tasks from entering a deep sleep
+ * mode, as it might interfere with the suspend threads on other CPUs.
+ * This does not prevent the suspend threads from using cpuidle (only
+ * the idle tasks check this status).
+ */
+ cpuidle_pause();
+
+ /*
+ * Unpark the suspend threads. To avoid the main thread being preempted
+ * before all the threads have been unparked, the suspend threads will
+ * wait for the completion of suspend_threads_started.
+ */
+ for (i = 0; i < nb_threads; ++i)
+ kthread_unpark(threads[i]);
+ complete_all(&suspend_threads_started);
+
+ wait_for_completion(&suspend_threads_done);
+
+ cpuidle_resume();
+
+ /* Stop and destroy all threads, get return status. */
+ for (i = 0; i < nb_threads; ++i)
+ err += kthread_stop(threads[i]);
+
+ kfree(threads);
+ return err;
+}
+
+static int __init psci_checker(void)
+{
+ int ret;
+
+ /*
+ * Since we're in an initcall, we assume that all the CPUs that all
+ * CPUs that can be onlined have been onlined.
+ *
+ * The tests assume that hotplug is enabled but nobody else is using it,
+ * otherwise the results will be unpredictable. However, since there
+ * is no userspace yet in initcalls, that should be fine.
+ */
+ nb_available_cpus = num_online_cpus();
+
+ /* Check PSCI operations are set up and working. */
+ ret = psci_ops_check();
+ if (ret)
+ return ret;
+
+ pr_info("PSCI checker started using %u CPUs\n", nb_available_cpus);
+
+ pr_info("Starting hotplug tests\n");
+ ret = hotplug_tests();
+ if (ret == 0)
+ pr_info("Hotplug tests passed OK\n");
+ else if (ret > 0)
+ pr_err("%d error(s) encountered in hotplug tests\n", ret);
+ else {
+ pr_err("Out of memory\n");
+ return ret;
+ }
+
+ pr_info("Starting suspend tests (%d cycles per state)\n",
+ NUM_SUSPEND_CYCLE);
+ ret = suspend_tests();
+ if (ret == 0)
+ pr_info("Suspend tests passed OK\n");
+ else if (ret > 0)
+ pr_err("%d error(s) encountered in suspend tests\n", ret);
+ else {
+ switch (ret) {
+ case -ENOMEM:
+ pr_err("Out of memory\n");
+ break;
+ case -ENODEV:
+ pr_warn("Could not start suspend tests on any CPU\n");
+ break;
+ }
+ }
+
+ pr_info("PSCI checker completed\n");
+ return ret < 0 ? ret : 0;
+}
+late_initcall(psci_checker);
--
2.9.3
^ permalink raw reply related
* [PATCH V6 3/5] PCI: thunder-pem: Allow to probe PEM-specific register range for ACPI case
From: Gabriele Paoloni @ 2016-09-21 14:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160920191744.GA4941@localhost>
Hi Bjorn
[...]
>
> If future hardware is completely ECAM-compliant and we don't need any
> more MCFG quirks, that would be great.
>
> But we'll still need to describe that memory-mapped config space
> somewhere. If that's done with PNP0C02 or similar devices (as is done
> on my x86 laptop), we'd be all set.
>
> If we need to work around firmware in the field that doesn't do that,
> one possibility is a PNP quirk along the lines of
> quirk_amd_mmconfig_area().
So, if my understanding is correct, for platforms that have not been
shipped yet you propose to use PNP0C02 in the ACPI table in order to
declare a motherboard reserved resource whereas for shipped platforms
you propose to have a quirk along pnp_fixups in order to track the
resource usage even if values are hardcoded...correct?
Before Tomasz came up with this patchset we had a call between the vendors
involved in this PCI quirks saga and other guys from Linaro and ARM.
Lorenzo summarized the outcome as in the following link
http://lkml.iu.edu/hypermail/linux/kernel/1606.2/03344.html
Since this quirks mechanism has been discussed for quite a long time now
IMHO it would be good to have a last call including also you (Bjorn) so
that we can all agree on what to do and we avoid changing our drivers again
and again...
What do you think?
Thanks
Gab
>
> Bjorn
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox