* [PATCHv2] PCI: QDF2432 32 bit config space accessors
From: Bjorn Helgaas @ 2016-11-02 16:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161102160820.GA6568@bhelgaas-glaptop.roam.corp.google.com>
On Wed, Nov 02, 2016 at 11:08:20AM -0500, Bjorn Helgaas wrote:
> On Tue, Nov 01, 2016 at 07:06:31AM -0600, cov at codeaurora.org wrote:
> > Hi Bjorn,
> >
> > On 2016-10-31 15:48, Bjorn Helgaas wrote:
> > >On Wed, Sep 21, 2016 at 06:38:05PM -0400, Christopher Covington wrote:
> > >>The Qualcomm Technologies QDF2432 SoC does not support accesses
> > >>smaller
> > >>than 32 bits to the PCI configuration space. Register the appropriate
> > >>quirk.
> > >>
> > >>Signed-off-by: Christopher Covington <cov@codeaurora.org>
> > >
> > >Hi Christopher,
> > >
> > >Can you rebase this against v4.9-rc1? It no longer applies to my tree.
> >
> > I apologize for not being clearer. This patch depends on:
> >
> > PCI/ACPI: Extend pci_mcfg_lookup() responsibilities
> > PCI/ACPI: Check platform-specific ECAM quirks
> >
> > These patches from Tomasz Nowicki were previously in your pci/ecam-v6
> > branch, but that seems to have come and gone. How would you like to
> > proceed?
>
> Oh yes, that's right, I forgot that connection. I'm afraid I kind of
> dropped the ball on that thread, so I went back and read through it
> again.
>
> I *think* the current state is:
>
> - I'm OK with the first two patches that add the quirk
> infrastructure.
>
> - My issue with the last three patches that add ThunderX quirks is
> that there's no generic description of the ECAM address space.
>
> So if I understand correctly, your Qualcomm patch depends only on the
> first two patches.
I put those first two patches and yours on pci/ecam-v6 and pushed it
again, so you can check it out.
Bjorn
^ permalink raw reply
* flush_dcache_page() in ARM vs ARM64
From: Catalin Marinas @ 2016-11-02 16:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161102132714.GA1326@lnxartpec.se.axis.com>
On Wed, Nov 02, 2016 at 02:27:14PM +0100, Rabin Vincent wrote:
> ARMv7-A and ARMv8-A are, as far as I can see, identical in which cache
> behaviours they support. The data cache has to behave as PIPT while for
> the instruction cache, PIPT, VIPT, and ASIC-tagged VIVT behaviours are
> supported. See section B3.11 of the ARMv7-A ARM and section D4.9 of the
> ARMv8-A ARM.
>
> Both ARMv7-A with Multiprocessing Extensions and ARMv8-A broadcast cache
> maintenance operations to other cores. See B2.2.5 of the ARMv7-A ARM
> and D7.2.57 of the ARMv8-A ARM.
>
> Both arch/arm/ (for ARMv6+) and arch/arm64/ define PG_arch_1 to be
> PG_dcache_clean and use it to postpone flushing from flush_dcache_page()
> to set_pte_at(). See arch/{arm,arm64}/mm/flush.c.
>
> However, arch/arm64/'s flush_dcache_page() is implemented like this:
>
> void flush_dcache_page(struct page *page)
> {
> if (test_bit(PG_dcache_clean, &page->flags))
> clear_bit(PG_dcache_clean, &page->flags);
> }
arm64 had a similar implementation to arm until commit b5b6c9e9149d
("arm64: Avoid cache flushing in flush_dcache_page()").
> Why does arch/arm/ flush the data cache area in flush_dcache_page() for
> the (!mapping || page_mapcount(page)) case even on ARMv7+ME, while
> arch/arm64/ doesn't for ARMv8?
IIRC, the reason was D-cache aliases which have disappeared from ARMv7.
> Why does arch/arm/ invalidate the instruction cache in
> flush_dcache_page() for the (mapping && page_count(page)) case even for
> ARMv7+ME, while arch/arm64/ doesn't for ARMv8?
I guess no-one updated it for non-aliasing caches.
> What would break with the following patch?
>
> diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
> index 3cced84..f1e6190 100644
> --- a/arch/arm/mm/flush.c
> +++ b/arch/arm/mm/flush.c
> @@ -327,6 +327,12 @@ void flush_dcache_page(struct page *page)
> if (page == ZERO_PAGE(0))
> return;
>
> + if (!cache_ops_need_broadcast() && cache_is_vipt_nonaliasing()) {
> + if (test_bit(PG_dcache_clean, &page->flags))
> + clear_bit(PG_dcache_clean, &page->flags);
> + return;
> + }
> +
> mapping = page_mapping(page);
>
> if (!cache_ops_need_broadcast() &&
This should work. Note that the test_bit() is just an optimisation I
borrowed from powerpc, not sure it has any noticeable impact (you could
as well just do the clear_bit()).
--
Catalin
^ permalink raw reply
* [PATCHv2] PCI: QDF2432 32 bit config space accessors
From: Sinan Kaya @ 2016-11-02 16:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161102160820.GA6568@bhelgaas-glaptop.roam.corp.google.com>
Hi Bjorn,
On 11/2/2016 12:08 PM, Bjorn Helgaas wrote:
> On Tue, Nov 01, 2016 at 07:06:31AM -0600, cov at codeaurora.org wrote:
>> Hi Bjorn,
>>
>> On 2016-10-31 15:48, Bjorn Helgaas wrote:
>>> On Wed, Sep 21, 2016 at 06:38:05PM -0400, Christopher Covington wrote:
>>>> The Qualcomm Technologies QDF2432 SoC does not support accesses
>>>> smaller
>>>> than 32 bits to the PCI configuration space. Register the appropriate
>>>> quirk.
>>>>
>>>> Signed-off-by: Christopher Covington <cov@codeaurora.org>
>>>
>>> Hi Christopher,
>>>
>>> Can you rebase this against v4.9-rc1? It no longer applies to my tree.
>>
>> I apologize for not being clearer. This patch depends on:
>>
>> PCI/ACPI: Extend pci_mcfg_lookup() responsibilities
>> PCI/ACPI: Check platform-specific ECAM quirks
>>
>> These patches from Tomasz Nowicki were previously in your pci/ecam-v6
>> branch, but that seems to have come and gone. How would you like to
>> proceed?
>
> Oh yes, that's right, I forgot that connection. I'm afraid I kind of
> dropped the ball on that thread, so I went back and read through it
> again.
>
> I *think* the current state is:
>
> - I'm OK with the first two patches that add the quirk
> infrastructure.
>
> - My issue with the last three patches that add ThunderX quirks is
> that there's no generic description of the ECAM address space.
>
> So if I understand correctly, your Qualcomm patch depends only on the
> first two patches.
>
> Then the question is how the Qualcomm ECAM address space is described.
> Your quirk overrides the default pci_generic_ecam_ops with the
> &pci_32b_ops, but it doesn't touch the address space part, so I assume
> the bus ranges and corresponding address space in your MCFG is
> correct. So far, so good.
Qualcomm ECAM space includes both the root port and the endpoint address
space with a single contiguous 256 MB address space described in MCFG table.
There is no need to describe additional resources like PNP0C02.
The only thing we missed was 8/16 bits access support on the root port.
That's why, we need Cov's patch.
>
> Is there also an ACPI device that contains that space in _CRS? I
> think we concluded that the standard solution is to describe this with
> a PNP0C02 device.
>
> Would you mind opening a bugzilla at bugzilla.kernel.org and attaching
> the dmesg log, /proc/iomem, and maybe a DSDT dump? I'd like to have
> something to point at to say "if you need an MCFG quirk, you need the
> MCFG bit and *also* these other related ACPI device bits, and here's
> how it should be done."
>
> Bjorn
>
--
Sinan Kaya
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
* [PATCH v7 0/7] arm/arm64: vgic: Implement API for vGICv3 live migration
From: Christoffer Dall @ 2016-11-02 16:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAMJs5B8aLZCDeAVhJN7Cajt4PL4HBtXc3BiNzMyjK1tOSjMwmg@mail.gmail.com>
Hi Vijay,
On Thu, Oct 06, 2016 at 02:14:13PM +0200, Christoffer Dall wrote:
> Hi Vijay,
>
> On Wed, Oct 5, 2016 at 4:33 PM, Vijay Kilari <vijay.kilari@gmail.com> wrote:
> > Hi Marc, Christoffer,
> >
> > Do you have any review comments on this patch set?.
> >
> Since we missed the 4.9 merge window there is no urgency just yet, and
> I'm tied up with other things for a while, and Marc is away this week
> on holiday. I'm sure he'll have a look when he's back and I'll follow
> up later.
>
I think it would be useful for you to post a v7 of your patch set
rebased v4.9-rcX and addressing the issue you found yourself with a
wrong parameter.
Then I'll have another look.
Thanks,
-Christoffer
^ permalink raw reply
* [PATCH] coresight: Add support for ARM Coresight STM-500
From: Suzuki K Poulose @ 2016-11-02 16:19 UTC (permalink / raw)
To: linux-arm-kernel
Add the PIDs for STM-500 to the known STM devices list.
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Chunyan Zhang <zhang.chunyan@linaro.org>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
drivers/hwtracing/coresight/coresight-stm.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index 49e0f1b..3524452 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -920,6 +920,11 @@ static struct amba_id stm_ids[] = {
.mask = 0x0003ffff,
.data = "STM32",
},
+ {
+ .id = 0x0003b963,
+ .mask = 0x0003ffff,
+ .data = "STM500",
+ },
{ 0, 0},
};
--
2.7.4
^ permalink raw reply related
* [PATCH v14 00/16] KVM PCIe/MSI passthrough on ARM/ARM64
From: Auger Eric @ 2016-11-02 16:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <452b773f-826b-61cd-55e3-018ecb34b503@arm.com>
Hi Robin,
On 24/10/2016 21:39, Robin Murphy wrote:
> On 21/10/16 10:26, Auger Eric wrote:
>> Hi Will,
>>
>> On 20/10/2016 19:32, Will Deacon wrote:
>>> Hi Eric,
>>>
>>> Thanks for posting this.
>>>
>>> On Wed, Oct 12, 2016 at 01:22:08PM +0000, Eric Auger wrote:
>>>> This is the second respin on top of Robin's series [1], addressing Alex' comments.
>>>>
>>>> Major changes are:
>>>> - MSI-doorbell API now is moved to DMA IOMMU API following Alex suggestion
>>>> to put all API pieces at the same place (so eventually in the IOMMU
>>>> subsystem)
>>>> - new iommu_domain_msi_resv struct and accessor through DOMAIN_ATTR_MSI_RESV
>>>> domain with mirror VFIO capability
>>>> - more robustness I think in the VFIO layer
>>>> - added "iommu/iova: fix __alloc_and_insert_iova_range" since with the current
>>>> code I failed allocating an IOVA page in a single page domain with upper part
>>>> reserved
>>>>
>>>> IOVA range exclusion will be handled in a separate series
>>>>
>>>> The priority really is to discuss and freeze the API and especially the MSI
>>>> doorbell's handling. Do we agree to put that in DMA IOMMU?
>>>>
>>>> Note: the size computation does not take into account possible page overlaps
>>>> between doorbells but it would add quite a lot of complexity i think.
>>>>
>>>> Tested on AMD Overdrive (single GICv2m frame) with I350 VF assignment.
>>>
>>> Marc, Robin and I sat down and had a look at the series and, whilst it's
>>> certainly addressing a problem that we desperately want to see fixed, we
>>> think that it's slightly over-engineering in places and could probably
>>> be simplified in the interest of getting something upstream that can be
>>> used as a base, on which the ABI can be extended as concrete use-cases
>>> become clear.
>>>
>>> Stepping back a minute, we're trying to reserve some of the VFIO virtual
>>> address space so that it can be used by devices to map their MSI doorbells
>>> using the SMMU. With your patches, this requires that (a) the kernel
>>> tells userspace about the size and alignment of the doorbell region
>>> (MSI_RESV) and (b) userspace tells the kernel the VA-range that can be
>>> used (RESERVED_MSI_IOVA).
>>>
>>> However, this is all special-cased for MSI doorbells and there are
>>> potentially other regions of the VFIO address space that are reserved
>>> and need to be communicated to userspace as well. We already know of
>>> hardware where the PCI RC intercepts p2p accesses before they make it
>>> to the SMMU, and other hardware where the MSI doorbell is at a fixed
>>> address. This means that we need a mechanism to communicate *fixed*
>>> regions of virtual address space that are reserved by VFIO. I don't
>>> even particularly care if VFIO_MAP_DMA enforces that, but we do need
>>> a way to tell userspace "hey, you don't want to put memory here because
>>> it won't work well with devices".
>>
>> I think we all agree on this. Exposing an API to the user space
>> reporting *fixed* reserved IOVA ranges is a requirement anyway. The
>> problem was quite clearly stated by Alex in
>> http://lkml.iu.edu/hypermail/linux/kernel/1610.0/03308.html
>> (VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE)
>>
>> I started working on this VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE
>> capability but to me and I think according to Alex, it was a different
>> API from MSI_RESV.
>>
>>>
>>> In that case, we end up with something like your MSI_RESV capability,
>>> but actually specifying a virtual address range that is simply not to
>>> be used by MAP_DMA -- we don't say anything about MSIs. Now, taking this
>>> to its logical conclusion, we no longer need to distinguish between
>>> remappable reserved regions and fixed reserved regions in the ABI.
>>> Instead, we can have the kernel allocate the virtual address space for
>>> the remappable reserved regions (probably somewhere in the bottom 4GB)
>>> and expose them via the capability.
>>
>>
>> If I understand correctly you want the host to arbitrarily choose where
>> it puts the iovas reserved for MSI and not ask the userspace.
>>
>> Well so we are back to the discussions we had in Dec 2015 (see Marc's
>> answer in http://thread.gmane.org/gmane.comp.emulators.kvm.arm.devel/3858).
>
> To an extent, yes, however the difference is that we now know we
> definitely have to deal with situations in which userspace *cannot* be
> in total control of the memory map, and that changes the game:
>
> _________
> / \
> / Fixed \
> / things (A) \
> ( _________ )
> \ / MSI \ /
> X doorbells X
> / \___(B)___/ \
> ( )
> \ Remappable /
> \ things (C)/
> \_________/
>
> In the absence of A, then B == C so it was very hard to not want to
> implement C. As soon as A *has* to be implemented for other reasons,
> then that is also sufficient to encompass B. C can still be implemented
> later as a nice-to-have, but is not necessary to get B off the ground.
>
>> - So I guess you will init an iova_domain seomewhere below the 4GB to
>> allocate the MSIs. what size are you going to choose. Don't you have the
>> same need to dimension the iova range.
>> - we still need to assess the MSI assignment safety. How will we compute
>> safety for VFIO?
>
> Absolutely. We're talking in general terms of the userspace ABI here,
> although that can't help but colour the underlying implementation
> decisions.
Sorry for the delay I was out of the office last week.
The userspace ABI to retrieve reserved regions is the *easy* part. It is
based on VFIO capability chain and I have an RFC ready.
Of course the VFIO internals still have to handle the
> specific case of MSIs, but that's basically no more than this:
>
> static bool msi_isolation = true; /* until proven otherwise */
> static unsigned long msi_remap_virt_base = 0x08000000; /* fits QEMU */
> static size_t msi_remap_size;
>
> vfio_msi_thing_callback(thing) {
> msi_remap_size += thing->info.size;
> msi_isolation &= thing->info.flags & PROVIDES_ISOLATION;
> }
>
> vfio_msi_init(...) {
> ...
> #ifdef CONFIG_X86
> msi_remap_virt_base = 0xfee00000;
> msi_remap_size = 0x100000;
> msi_isolation = irq_remapping_enabled;
> #else
> irq_for_each_msi_thing(vfio_msi_thing_callback);
> #endif
> ...
> }
>
> vfio_attach_group(...) {
> ...
> if (!msi_isolation && !allow_unsafe_interrupts)
> return -ENOWAY;
> ...
> get_msi_region_cookie(domain, msi_remap_base, msi_remap_size);
> ...
> }
I doubt Alex will accept to put that code in VFIO. He suggested in the
past to use the IOMMU API to retrieve the reserved region(s).
what about adding a reserved_regions list in iommu_domain and add a new
iommu_ops, something like
void add_reserved_regions(struct iommu_domain *, struct device *dev)
whose role would be to populate the list. This add_reserved_regions()
would be called on __iommu_attach_device. The list would be emptied on
iommu_domain_free().
arm-smmu cb implementation would be in charge of
- computing non ACS PCI host bridge windows from @dev,
- computing msi_rebase_map/size computation
on x86, cb would simply populate the MSI window.
vfio would lookup the iommu domain reserved_regions list on
VFIO_IOMMU_GET_INFO
Drawback of this approach is the security aspect is not handled by the
IOMMU API.
Note that combining v14 series and this one would implement everything I
think + giving the flexibility for the userspace to choose where it put
things. But well, LPC discussions will bring the last word obviously.
>
> And when a well-behaved userspace queries the reserved regions, that
> base address and size is just one of potentially several that it should
> get back. It's that "querying the reserved regions" bit that needs to be
> gotten right first time.
>
> Note that at this point I'm no longer even overly bothered about the
> details of irq_for_each_msi_thing(), as it's an internal kernel
> interface and thus malleable, although obviously the simpler the better.
> I have to say Punit's idea of iterating irq_domains does actually look
> really neat and tidy as a proof-of-concept, and also makes me think off
> on a tangent that it would be sweet to be able to retrieve base+size
> from dev->msi_domain to pre-allocate MSI pages in default domains, and
> obviate the compose 'failure' case.
As Punit mentionned, the natural place where the msi doorbell base,
size and irq_remapping can be retrieved looks to be the irqchip itself.
It works perfectly fine for v2m and its. Hence my first attempt to use a
cb at this level (irqchip msi_doorbell_info up to v11).
Adding a cb at irq_domain level looks quite impractical to me to
retrieve the info. Actually I don't see how to manage that without
adding new fields in irq_domain struct. If you have any suggestion,
please let me know.
Thanks
Eric
>
>> This simplifies things in the
>>> following ways:
>>>
>>> * You don't need to keep track of MSI vs DMA addresses in the VFIO rbtree
>> right: I guess you rely on iommu_map to return an error in case the iova
>> is already mapped somewhere else.
>>> * You don't need to try collapsing doorbells into a single region
>> why? at host level I guess you will init a single iova domain?
>
> Yeah, right now this one goes either way - as things stand, it does make
> life easier on the host side to make a single region to hang off the
> back of the current iova_cookie magic, and as illustrated above it's
> possibly the most trivial part of the whole thing, but the point is we
> still don't *need* to. Since a userspace ABI for generic reservations
> has to be able handle more than one region for the sake of non-MSI
> things, we'd be free to change the kernel-side implementation in future
> to just report multiple doorbells as individual regions - for starters,
> if and when we add dynamic reservations and userspace gets to pick its
> own IOVAs for those, it'll be a damn sight easier *not* to coalesce
> everything.
>
>>> * You don't need a special MAP flavour to map MSI doorbells
>> right
>>> * The ABI is reusable for PCI p2p and fixed doorbells
>> right
>>
>> Aren't we moving the issue at user-space? Currently QEMU mach-virt
>> address space is fully static. Adapting mach-virt to adjust to host
>> constraints is not straightforward. It is simple to reject the
>> assignment in case of collision but more difficult to react positively.
>
> The point is that we *have* to move at least some of the issue to
> userspace, and by then I'm struggling to see any real difference between
> these situations:
>
> a) QEMU asks VFIO to map some pages for DMA, gets an error back because
> VFIO detects it conflicts with a reserved region, and gives up.
> b) QEMU starts by asking VFIO what regions are reserved, realises they
> will overlap with its hard-coded RAM address, and gives up.
>
> where (a) requires a bunch of kernel machinery to second-guess
> userspace, while (b) simply relies on userspace not being broken. And if
> userspace fails at not being broken, then we simply retain the behaviour
> which actually happens right now:
>
> c) QEMU maps some pages for DMA at the same address as PCI config space
> on the underlying hardware. Hilarity ensues.
>
> Of course, userspace could be anything other than QEMU as well, so it's
> not necessarily second-guessable at all; maybe we make the arbitrary
> msi_remap_virt_base a VFIO module parameter to be more accommodating.
> Who knows, maybe it turns out that's enough to keep users happy and we
> never need to implement fully dynamic reservations.
>
> Robin.
>
>>> I really think it would make your patch series both generally useful and
>>> an awful lot smaller, whilst leaving the door open to ABI extension on
>>> a case-by-case basis when we determine that it's really needed.
>>
>> I would like to have a better understanding of how you assess the
>> security and dimension the iova domain. This is the purpose of msi
>> doorbell registration, which is not neat at all I acknowledge but well I
>> did not find any other solution and did not get any other suggestion.
>> Besides I think the per-cpu thing is over-engineered and this can
>> definitively be simplified.
>>
>> VFIO part was reviewed by Alex and I don't have the impression that this
>> is the blocking part. besides there is on iova.c fix,
>> IOMMU_CAP_INTR_REMAP removal; so is it really over-complicated?
>>
>> Thanks
>>
>> Eric
>>
>>>
>>> Thoughts?
>>>
>>> Will
>>>
>>> _______________________________________________
>>> linux-arm-kernel mailing list
>>> linux-arm-kernel at lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>
>>
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply
* [PATCH] ARM: DT: stm32: move dma translation to board files
From: Alexandre Torgue @ 2016-11-02 16:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAF3+TqeiPjtG7Fs1-cpbcHGxxtSS1UOGD=S=sggpaZahFLJhEQ@mail.gmail.com>
On 11/02/2016 05:07 PM, Bruno Herrera wrote:
> Hi
>
> On Wed, Nov 2, 2016 at 12:32 PM, Alexandre Torgue
> <alexandre.torgue@st.com> wrote:
>> Hi
>>
>> On 10/31/2016 07:58 PM, Rados?aw Pietrzyk wrote:
>>>
>>> I think wlcore driver searches dma-ranges in its parent that's why sdio
>>> node needs it.
>>
>>
>> Yes I agree. In this case it is needed as you have subnode in sdio node.
>> So IMO empty dma-ranges could be removed from ethernet and usb node, but
>> kept in future sdio subnode.
>
> Now it is clear.
Nice. Can I add your tested-by ?
>
>>
>> Bruno,
>> Do you plan to push sdio support ?
>
> Yes I do, but I'm not sure how long it will take. The I had to
> change(and hack) the mmci code because I could not get the ID from
> STM32 SDIO IP.
> My current WIP is at @
> https://github.com/mcoquelin-stm32/afboot-stm32/pull/4#issuecomment-247571615
> I know Andrea Merello is also working on that (and he probably has a
> more complete patch).
>
>>
>>
>>
>>>
>>> 2016-10-31 17:41 GMT+01:00 Bruno Herrera <bruherrera@gmail.com
>>> <mailto:bruherrera@gmail.com>>:
>>>
>>> On Mon, Oct 31, 2016 at 12:14 PM, Rados?aw Pietrzyk
>>> <radoslaw.pietrzyk at gmail.com <mailto:radoslaw.pietrzyk@gmail.com>>
>>> wrote:
>>> > This is weird because dma ddresses are recalculated using parent's
>>> > dma-ranges property and soc already has it so there should be
>>> absolutely no
>>> > problem.
>>>
>>> These are my DTS and DTSI file.
>>> >
>>> > 2016-10-31 11:27 GMT+01:00 Bruno Herrera <bruherrera@gmail.com
>>> <mailto:bruherrera@gmail.com>>:
>>> >>
>>> >> On Fri, Oct 28, 2016 at 5:09 AM, Rados?aw Pietrzyk
>>> >> <radoslaw.pietrzyk@gmail.com
>>> <mailto:radoslaw.pietrzyk@gmail.com>> wrote:
>>> >> > Have you defined your sdio node within soc node ?
>>> >>
>>> >> It is in the SOC node of the DSTI file.
>>> >>
>>> >> >
>>> >> > 2016-10-27 14:57 GMT+02:00 Bruno Herrera <bruherrera@gmail.com
>>> <mailto:bruherrera@gmail.com>>:
>>> >> >>
>>> >> >> Hi Alex,
>>> >> >>
>>> >> >> On Thu, Oct 27, 2016 at 10:21 AM, Alexandre Torgue
>>> >> >> <alexandre.torgue at st.com <mailto:alexandre.torgue@st.com>>
>>> wrote:
>>> >> >> > Hi Bruno,
>>> >> >> >
>>> >> >> >
>>> >> >> > On 10/27/2016 12:43 PM, Bruno Herrera wrote:
>>> >> >> >>
>>> >> >> >> Hi Alex,
>>> >> >> >>
>>> >> >> >> On Wed, Oct 26, 2016 at 7:09 AM, Alexandre Torgue
>>> >> >> >> <alexandre.torgue at st.com <mailto:alexandre.torgue@st.com>>
>>> wrote:
>>> >> >> >>>
>>> >> >> >>> Hi Bruno,
>>> >> >> >>>
>>> >> >> >>> On 10/25/2016 11:06 PM, Bruno Herrera wrote:
>>> >> >> >>>>
>>> >> >> >>>>
>>> >> >> >>>> Hi Alexandre,
>>> >> >> >>>>
>>> >> >> >>>>>
>>> >> >> >>>>> stm32f469-disco and stm32f429-eval boards use SDRAM
>>> start address
>>> >> >> >>>>> remapping
>>> >> >> >>>>> (to @0) to boost performances. A DMA translation through
>>> >> >> >>>>> "dma-ranges"
>>> >> >> >>>>> property was needed for other masters than the M4 CPU.
>>> >> >> >>>>> stm32f429-disco doesn't use remapping so doesn't need
>>> this DMA
>>> >> >> >>>>> translation.
>>> >> >> >>>>> This patches moves this DMA translation definition from
>>> stm32f429
>>> >> >> >>>>> soc
>>> >> >> >>>>> file
>>> >> >> >>>>> to board files.
>>> >> >> >>>>>
>>> >> >> >>>>> Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com
>>> <mailto:alexandre.torgue@st.com>>
>>>
>>> >> >> >>>>>
>>> >> >> >>>>> diff --git a/arch/arm/boot/dts/stm32429i-eval.dts
>>> >> >> >>>>> b/arch/arm/boot/dts/stm32429i-eval.dts
>>> >> >> >>>>> index 13c7cd2..a763c15 100644
>>> >> >> >>>>> --- a/arch/arm/boot/dts/stm32429i-eval.dts
>>> >> >> >>>>> +++ b/arch/arm/boot/dts/stm32429i-eval.dts
>>> >> >> >>>>> @@ -82,6 +82,10 @@
>>> >> >> >>>>> };
>>> >> >> >>>>> };
>>> >> >> >>>>>
>>> >> >> >>>>> + soc {
>>> >> >> >>>>> + dma-ranges = <0xc0000000 0x0 0x10000000>;
>>> >> >> >>>>> + };
>>> >> >> >>>>> +
>>> >> >> >>>>> usbotg_hs_phy: usbphy {
>>> >> >> >>>>> #phy-cells = <0>;
>>> >> >> >>>>> compatible = "usb-nop-xceiv";
>>> >> >> >>>>
>>> >> >> >>>>
>>> >> >> >>>>
>>> >> >> >>>> Shouldn't also the peripheral dma-ranges property move to
>>> board
>>> >> >> >>>> specific
>>> >> >> >>>> too?
>>> >> >> >>>> I had this patch for while but I didn't had the time to
>>> submit:
>>> >> >> >>>
>>> >> >> >>>
>>> >> >> >>>
>>> >> >> >>> Well spot I forgot it. Actually, discussing with Arnd
>>> ysterday on
>>> >> >> >>> IIRC,
>>> >> >> >>> empty dma-ranges is not needed. Can you test on your side by
>>> >> >> >>> removing
>>> >> >> >>> dma-ranges in usb node please ?
>>> >> >> >>
>>> >> >> >> Unfortunately will take a time for me to set up this
>>> environment on
>>> >> >> >> the STM32F4-EVAL board.
>>> >> >> >> And on the discovery boards we dont have this scenario.
>>> That was the
>>> >> >> >> main reason I did not submit the patch right away.
>>> >> >> >> My conclusion and I might be wrong but is based on the my
>>> tests with
>>> >> >> >> SDIO device at STM32F469I-DISCO board.
>>> >> >> >>
>>> >> >> >> I started this issue as discussion at ST Forum but Maxime
>>> gave me
>>> >> >> >> the
>>> >> >> >> hint.
>>> >> >> >>
>>> >> >> >>
>>> >> >> >>
>>> >> >> >>
>>> >> >> >>
>>>
>>> https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy%2est%2ecom%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%2fDMA2%20and%20SYSCFG_MEMRMP%20relationship&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=44
>>>
>>> <https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy%2est%2ecom%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%2fDMA2%20and%20SYSCFG_MEMRMP%20relationship&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=44>
>>> >> >> >>
>>> >> >> >>> I will push a v2 by removing empty dma-ranges if tests are
>>> ok in
>>> >> >> >>> your
>>> >> >> >>> side.
>>> >> >> >>
>>> >> >> >>
>>> >> >> >> From my understating/conclusion is: when empty
>>> property(dma-ranges)
>>> >> >> >> is
>>> >> >> >> the device node, the mapping will be taken in consideration
>>> when
>>> >> >> >> using
>>> >> >> >> DMA otherwise the mapping is ignored.
>>> >> >> >> And in the SDIO case it is needed for DEV->MEM(SDRAM) and
>>> >> >> >> MEM(SDRAM)->DEV. If it is not the case for the devices in
>>> question
>>> >> >> >> so
>>> >> >> >> I suppose it can work without the property.
>>> >> >> >
>>> >> >> >
>>> >> >> > For sure translation has to be done but I'm not sure that an
>>> empty
>>> >> >> > "dma-ranges" is needed in device node to activate it. For
>>> Ethernet
>>> >> >> > empty
>>> >> >> > "dma-ranges" is not needed. I will try with usb.
>>> >> >>
>>> >> >> In the case of SDIO it is needed. As example this is my
>>> working SDIO
>>> >> >> node:
>>> >> >>
>>> >> >> sdio: sdio at 40012c00 {
>>> >> >> compatible = "arm,pl18x", "arm,primecell";
>>> >> >> arm,primecell-periphid = <0x00480181>;
>>> >> >> reg = <0x40012c00 0x400>;
>>> >> >> dmas = <&dma2 6 4 0x10400 0x3>, /* Logical - DevToMem */
>>> >> >> <&dma2 3 4 0x10400 0x3>; /* Logical - MemToDev */
>>> >> >> dma-names = "rx", "tx";
>>> >> >> clocks = <&rcc 0 171>;
>>> >> >> clock-names = "apb_pclk";
>>> >> >> interrupts = <49>;
>>> >> >> status = "disabled";
>>> >> >> };
>>> >> >>
>>> >> >> &sdio {
>>> >> >> status = "okay";
>>> >> >> vmmc-supply = <&wlan_en>;
>>> >> >> bus-width = <4>;
>>> >> >> max-frequency = <24000000>;
>>> >> >> pinctrl-names = "default";
>>> >> >> pinctrl-0 = <&sdio_pins>;
>>> >> >> ti,non-removable;
>>> >> >> ti,needs-special-hs-handling;
>>> >> >> dma-ranges;
>>> >> >> cap-power-off-card;
>>> >> >> keep-power-in-suspend;
>>> >> >>
>>> >> >> #address-cells = <1>;
>>> >> >> #size-cells = <0>;
>>> >> >> wlcore: wlcore at 0 {
>>> >> >> compatible = "ti,wl1835";
>>> >> >> reg = <2>;
>>> >> >> interrupt-parent = <&gpioa>;
>>> >> >> interrupts = <8 IRQ_TYPE_EDGE_RISING>;
>>> >> >> };
>>> >> >> };
>>> >> >>
>>> >> >> >
>>> >> >> > alex
>>> >> >> >
>>> >> >> >
>>> >> >> >>
>>> >> >> >>>
>>> >> >> >>> Thanks in advance
>>> >> >> >>> Alex
>>> >> >> >>>
>>> >> >> >>>
>>> >> >> >>>>
>>> >> >> >>>> Author: Bruno Herrera <bruherrera@gmail.com
>>> <mailto:bruherrera@gmail.com>>
>>>
>>> >> >> >>>> Date: Sun Oct 16 14:50:00 2016 -0200
>>> >> >> >>>>
>>> >> >> >>>> ARM: DT: STM32: Use dma-ranges property per board not
>>> at dtsi
>>> >> >> >>>> file
>>> >> >> >>>>
>>> >> >> >>>> diff --git a/arch/arm/boot/dts/stm32429i-eval.dts
>>> >> >> >>>> b/arch/arm/boot/dts/stm32429i-eval.dts
>>> >> >> >>>> index 6bfc595..2a22a82 100644
>>> >> >> >>>> --- a/arch/arm/boot/dts/stm32429i-eval.dts
>>> >> >> >>>> +++ b/arch/arm/boot/dts/stm32429i-eval.dts
>>> >> >> >>>> @@ -52,6 +52,10 @@
>>> >> >> >>>> model = "STMicroelectronics STM32429i-EVAL board";
>>> >> >> >>>> compatible = "st,stm32429i-eval", "st,stm32f429";
>>> >> >> >>>>
>>> >> >> >>>> + soc {
>>> >> >> >>>> + dma-ranges = <0xC0000000 0x0 0x10000000>;
>>> >> >> >>>> + };
>>> >> >> >>>> +
>>> >> >> >>>> chosen {
>>> >> >> >>>> bootargs = "root=/dev/ram rdinit=/linuxrc";
>>> >> >> >>>> stdout-path = "serial0:115200n8";
>>> >> >> >>>> @@ -96,6 +100,7 @@
>>> >> >> >>>>
>>> >> >> >>>> ðernet0 {
>>> >> >> >>>> status = "okay";
>>> >> >> >>>> + dma-ranges;
>>> >> >> >>>> pinctrl-0 = <ðernet0_mii>;
>>> >> >> >>>> pinctrl-names = "default";
>>> >> >> >>>> phy-mode = "mii-id";
>>> >> >> >>>> @@ -116,6 +121,7 @@
>>> >> >> >>>> };
>>> >> >> >>>>
>>> >> >> >>>> &usbotg_hs {
>>> >> >> >>>> + dma-ranges;
>>> >> >> >>>> dr_mode = "host";
>>> >> >> >>>> phys = <&usbotg_hs_phy>;
>>> >> >> >>>> phy-names = "usb2-phy";
>>> >> >> >>>> diff --git a/arch/arm/boot/dts/stm32f429.dtsi
>>> >> >> >>>> b/arch/arm/boot/dts/stm32f429.dtsi
>>> >> >> >>>> index 7d624a2..697a133 100644
>>> >> >> >>>> --- a/arch/arm/boot/dts/stm32f429.dtsi
>>> >> >> >>>> +++ b/arch/arm/boot/dts/stm32f429.dtsi
>>> >> >> >>>> @@ -59,7 +59,6 @@
>>> >> >> >>>> };
>>> >> >> >>>>
>>> >> >> >>>> soc {
>>> >> >> >>>> - dma-ranges = <0xc0000000 0x0 0x10000000>;
>>> >> >> >>>>
>>> >> >> >>>> timer2: timer at 40000000 {
>>> >> >> >>>> compatible = "st,stm32-timer";
>>> >> >> >>>> @@ -472,13 +471,11 @@
>>> >> >> >>>> st,syscon = <&syscfg 0x4>;
>>> >> >> >>>> snps,pbl = <8>;
>>> >> >> >>>> snps,mixed-burst;
>>> >> >> >>>> - dma-ranges;
>>> >> >> >>>> status = "disabled";
>>> >> >> >>>> };
>>> >> >> >>>>
>>> >> >> >>>> usbotg_hs: usb at 40040000 {
>>> >> >> >>>> compatible = "snps,dwc2";
>>> >> >> >>>> - dma-ranges;
>>> >> >> >>>> reg = <0x40040000 0x40000>;
>>> >> >> >>>> interrupts = <77>;
>>> >> >> >>>> clocks = <&rcc 0 29>;
>>> >> >> >>>>
>>> >> >> >>>>
>>> >> >> >>>>> diff --git a/arch/arm/boot/dts/stm32f429.dtsi
>>> >> >> >>>>> b/arch/arm/boot/dts/stm32f429.dtsi
>>> >> >> >>>>> index 0596d60..3a1cfdd 100644
>>> >> >> >>>>> --- a/arch/arm/boot/dts/stm32f429.dtsi
>>> >> >> >>>>> +++ b/arch/arm/boot/dts/stm32f429.dtsi
>>> >> >> >>>>> @@ -59,8 +59,6 @@
>>> >> >> >>>>> };
>>> >> >> >>>>>
>>> >> >> >>>>> soc {
>>> >> >> >>>>> - dma-ranges = <0xc0000000 0x0 0x10000000>;
>>> >> >> >>>>> -
>>> >> >> >>>>> timer2: timer at 40000000 {
>>> >> >> >>>>> compatible = "st,stm32-timer";
>>> >> >> >>>>> reg = <0x40000000 0x400>;
>>> >> >> >>>>> diff --git a/arch/arm/boot/dts/stm32f469-disco.dts
>>> >> >> >>>>> b/arch/arm/boot/dts/stm32f469-disco.dts
>>> >> >> >>>>> index 9e73656..c2213c0 100644
>>> >> >> >>>>> --- a/arch/arm/boot/dts/stm32f469-disco.dts
>>> >> >> >>>>> +++ b/arch/arm/boot/dts/stm32f469-disco.dts
>>> >> >> >>>>> @@ -64,6 +64,10 @@
>>> >> >> >>>>> aliases {
>>> >> >> >>>>> serial0 = &usart3;
>>> >> >> >>>>> };
>>> >> >> >>>>> +
>>> >> >> >>>>> + soc {
>>> >> >> >>>>> + dma-ranges = <0xc0000000 0x0 0x10000000>;
>>> >> >> >>>>> + };
>>> >> >> >>>>> };
>>> >> >> >>>>>
>>> >> >> >>>>> &clk_hse {
>>> >> >> >>>>> --
>>> >> >> >>>>
>>> >> >> >>>>
>>> >> >> >>>>
>>> >> >> >>>>
>>> >> >> >>>> Br.,
>>> >> >> >>>> Bruno
>>> >> >> >>>>
>>> >> >> >>>
>>> >> >> >
>>> >> >>
>>> >> >> _______________________________________________
>>> >> >> linux-arm-kernel mailing list
>>> >> >> linux-arm-kernel at lists.infradead.org
>>> <mailto:linux-arm-kernel@lists.infradead.org>
>>> >> >> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>> <http://lists.infradead.org/mailman/listinfo/linux-arm-kernel>
>>> >> >
>>> >> >
>>> >
>>> >
>>>
>>>
>>
^ permalink raw reply
* [PATCHv2] PCI: QDF2432 32 bit config space accessors
From: Bjorn Helgaas @ 2016-11-02 16:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <dbf40b73baa71155f39e13d6e51073aa@codeaurora.org>
On Tue, Nov 01, 2016 at 07:06:31AM -0600, cov at codeaurora.org wrote:
> Hi Bjorn,
>
> On 2016-10-31 15:48, Bjorn Helgaas wrote:
> >On Wed, Sep 21, 2016 at 06:38:05PM -0400, Christopher Covington wrote:
> >>The Qualcomm Technologies QDF2432 SoC does not support accesses
> >>smaller
> >>than 32 bits to the PCI configuration space. Register the appropriate
> >>quirk.
> >>
> >>Signed-off-by: Christopher Covington <cov@codeaurora.org>
> >
> >Hi Christopher,
> >
> >Can you rebase this against v4.9-rc1? It no longer applies to my tree.
>
> I apologize for not being clearer. This patch depends on:
>
> PCI/ACPI: Extend pci_mcfg_lookup() responsibilities
> PCI/ACPI: Check platform-specific ECAM quirks
>
> These patches from Tomasz Nowicki were previously in your pci/ecam-v6
> branch, but that seems to have come and gone. How would you like to
> proceed?
Oh yes, that's right, I forgot that connection. I'm afraid I kind of
dropped the ball on that thread, so I went back and read through it
again.
I *think* the current state is:
- I'm OK with the first two patches that add the quirk
infrastructure.
- My issue with the last three patches that add ThunderX quirks is
that there's no generic description of the ECAM address space.
So if I understand correctly, your Qualcomm patch depends only on the
first two patches.
Then the question is how the Qualcomm ECAM address space is described.
Your quirk overrides the default pci_generic_ecam_ops with the
&pci_32b_ops, but it doesn't touch the address space part, so I assume
the bus ranges and corresponding address space in your MCFG is
correct. So far, so good.
Is there also an ACPI device that contains that space in _CRS? I
think we concluded that the standard solution is to describe this with
a PNP0C02 device.
Would you mind opening a bugzilla at bugzilla.kernel.org and attaching
the dmesg log, /proc/iomem, and maybe a DSDT dump? I'd like to have
something to point at to say "if you need an MCFG quirk, you need the
MCFG bit and *also* these other related ACPI device bits, and here's
how it should be done."
Bjorn
^ permalink raw reply
* [PATCH] ARM: DT: stm32: move dma translation to board files
From: Bruno Herrera @ 2016-11-02 16:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <3e854414-f51d-3e59-6cee-142106cef40f@st.com>
Hi
On Wed, Nov 2, 2016 at 12:32 PM, Alexandre Torgue
<alexandre.torgue@st.com> wrote:
> Hi
>
> On 10/31/2016 07:58 PM, Rados?aw Pietrzyk wrote:
>>
>> I think wlcore driver searches dma-ranges in its parent that's why sdio
>> node needs it.
>
>
> Yes I agree. In this case it is needed as you have subnode in sdio node.
> So IMO empty dma-ranges could be removed from ethernet and usb node, but
> kept in future sdio subnode.
Now it is clear.
>
> Bruno,
> Do you plan to push sdio support ?
Yes I do, but I'm not sure how long it will take. The I had to
change(and hack) the mmci code because I could not get the ID from
STM32 SDIO IP.
My current WIP is at @
https://github.com/mcoquelin-stm32/afboot-stm32/pull/4#issuecomment-247571615
I know Andrea Merello is also working on that (and he probably has a
more complete patch).
>
>
>
>>
>> 2016-10-31 17:41 GMT+01:00 Bruno Herrera <bruherrera@gmail.com
>> <mailto:bruherrera@gmail.com>>:
>>
>> On Mon, Oct 31, 2016 at 12:14 PM, Rados?aw Pietrzyk
>> <radoslaw.pietrzyk at gmail.com <mailto:radoslaw.pietrzyk@gmail.com>>
>> wrote:
>> > This is weird because dma ddresses are recalculated using parent's
>> > dma-ranges property and soc already has it so there should be
>> absolutely no
>> > problem.
>>
>> These are my DTS and DTSI file.
>> >
>> > 2016-10-31 11:27 GMT+01:00 Bruno Herrera <bruherrera@gmail.com
>> <mailto:bruherrera@gmail.com>>:
>> >>
>> >> On Fri, Oct 28, 2016 at 5:09 AM, Rados?aw Pietrzyk
>> >> <radoslaw.pietrzyk@gmail.com
>> <mailto:radoslaw.pietrzyk@gmail.com>> wrote:
>> >> > Have you defined your sdio node within soc node ?
>> >>
>> >> It is in the SOC node of the DSTI file.
>> >>
>> >> >
>> >> > 2016-10-27 14:57 GMT+02:00 Bruno Herrera <bruherrera@gmail.com
>> <mailto:bruherrera@gmail.com>>:
>> >> >>
>> >> >> Hi Alex,
>> >> >>
>> >> >> On Thu, Oct 27, 2016 at 10:21 AM, Alexandre Torgue
>> >> >> <alexandre.torgue at st.com <mailto:alexandre.torgue@st.com>>
>> wrote:
>> >> >> > Hi Bruno,
>> >> >> >
>> >> >> >
>> >> >> > On 10/27/2016 12:43 PM, Bruno Herrera wrote:
>> >> >> >>
>> >> >> >> Hi Alex,
>> >> >> >>
>> >> >> >> On Wed, Oct 26, 2016 at 7:09 AM, Alexandre Torgue
>> >> >> >> <alexandre.torgue at st.com <mailto:alexandre.torgue@st.com>>
>> wrote:
>> >> >> >>>
>> >> >> >>> Hi Bruno,
>> >> >> >>>
>> >> >> >>> On 10/25/2016 11:06 PM, Bruno Herrera wrote:
>> >> >> >>>>
>> >> >> >>>>
>> >> >> >>>> Hi Alexandre,
>> >> >> >>>>
>> >> >> >>>>>
>> >> >> >>>>> stm32f469-disco and stm32f429-eval boards use SDRAM
>> start address
>> >> >> >>>>> remapping
>> >> >> >>>>> (to @0) to boost performances. A DMA translation through
>> >> >> >>>>> "dma-ranges"
>> >> >> >>>>> property was needed for other masters than the M4 CPU.
>> >> >> >>>>> stm32f429-disco doesn't use remapping so doesn't need
>> this DMA
>> >> >> >>>>> translation.
>> >> >> >>>>> This patches moves this DMA translation definition from
>> stm32f429
>> >> >> >>>>> soc
>> >> >> >>>>> file
>> >> >> >>>>> to board files.
>> >> >> >>>>>
>> >> >> >>>>> Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com
>> <mailto:alexandre.torgue@st.com>>
>>
>> >> >> >>>>>
>> >> >> >>>>> diff --git a/arch/arm/boot/dts/stm32429i-eval.dts
>> >> >> >>>>> b/arch/arm/boot/dts/stm32429i-eval.dts
>> >> >> >>>>> index 13c7cd2..a763c15 100644
>> >> >> >>>>> --- a/arch/arm/boot/dts/stm32429i-eval.dts
>> >> >> >>>>> +++ b/arch/arm/boot/dts/stm32429i-eval.dts
>> >> >> >>>>> @@ -82,6 +82,10 @@
>> >> >> >>>>> };
>> >> >> >>>>> };
>> >> >> >>>>>
>> >> >> >>>>> + soc {
>> >> >> >>>>> + dma-ranges = <0xc0000000 0x0 0x10000000>;
>> >> >> >>>>> + };
>> >> >> >>>>> +
>> >> >> >>>>> usbotg_hs_phy: usbphy {
>> >> >> >>>>> #phy-cells = <0>;
>> >> >> >>>>> compatible = "usb-nop-xceiv";
>> >> >> >>>>
>> >> >> >>>>
>> >> >> >>>>
>> >> >> >>>> Shouldn't also the peripheral dma-ranges property move to
>> board
>> >> >> >>>> specific
>> >> >> >>>> too?
>> >> >> >>>> I had this patch for while but I didn't had the time to
>> submit:
>> >> >> >>>
>> >> >> >>>
>> >> >> >>>
>> >> >> >>> Well spot I forgot it. Actually, discussing with Arnd
>> ysterday on
>> >> >> >>> IIRC,
>> >> >> >>> empty dma-ranges is not needed. Can you test on your side by
>> >> >> >>> removing
>> >> >> >>> dma-ranges in usb node please ?
>> >> >> >>
>> >> >> >> Unfortunately will take a time for me to set up this
>> environment on
>> >> >> >> the STM32F4-EVAL board.
>> >> >> >> And on the discovery boards we dont have this scenario.
>> That was the
>> >> >> >> main reason I did not submit the patch right away.
>> >> >> >> My conclusion and I might be wrong but is based on the my
>> tests with
>> >> >> >> SDIO device at STM32F469I-DISCO board.
>> >> >> >>
>> >> >> >> I started this issue as discussion at ST Forum but Maxime
>> gave me
>> >> >> >> the
>> >> >> >> hint.
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >>
>> >> >> >>
>>
>> https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy%2est%2ecom%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%2fDMA2%20and%20SYSCFG_MEMRMP%20relationship&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=44
>>
>> <https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy%2est%2ecom%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%2fDMA2%20and%20SYSCFG_MEMRMP%20relationship&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=44>
>> >> >> >>
>> >> >> >>> I will push a v2 by removing empty dma-ranges if tests are
>> ok in
>> >> >> >>> your
>> >> >> >>> side.
>> >> >> >>
>> >> >> >>
>> >> >> >> From my understating/conclusion is: when empty
>> property(dma-ranges)
>> >> >> >> is
>> >> >> >> the device node, the mapping will be taken in consideration
>> when
>> >> >> >> using
>> >> >> >> DMA otherwise the mapping is ignored.
>> >> >> >> And in the SDIO case it is needed for DEV->MEM(SDRAM) and
>> >> >> >> MEM(SDRAM)->DEV. If it is not the case for the devices in
>> question
>> >> >> >> so
>> >> >> >> I suppose it can work without the property.
>> >> >> >
>> >> >> >
>> >> >> > For sure translation has to be done but I'm not sure that an
>> empty
>> >> >> > "dma-ranges" is needed in device node to activate it. For
>> Ethernet
>> >> >> > empty
>> >> >> > "dma-ranges" is not needed. I will try with usb.
>> >> >>
>> >> >> In the case of SDIO it is needed. As example this is my
>> working SDIO
>> >> >> node:
>> >> >>
>> >> >> sdio: sdio at 40012c00 {
>> >> >> compatible = "arm,pl18x", "arm,primecell";
>> >> >> arm,primecell-periphid = <0x00480181>;
>> >> >> reg = <0x40012c00 0x400>;
>> >> >> dmas = <&dma2 6 4 0x10400 0x3>, /* Logical - DevToMem */
>> >> >> <&dma2 3 4 0x10400 0x3>; /* Logical - MemToDev */
>> >> >> dma-names = "rx", "tx";
>> >> >> clocks = <&rcc 0 171>;
>> >> >> clock-names = "apb_pclk";
>> >> >> interrupts = <49>;
>> >> >> status = "disabled";
>> >> >> };
>> >> >>
>> >> >> &sdio {
>> >> >> status = "okay";
>> >> >> vmmc-supply = <&wlan_en>;
>> >> >> bus-width = <4>;
>> >> >> max-frequency = <24000000>;
>> >> >> pinctrl-names = "default";
>> >> >> pinctrl-0 = <&sdio_pins>;
>> >> >> ti,non-removable;
>> >> >> ti,needs-special-hs-handling;
>> >> >> dma-ranges;
>> >> >> cap-power-off-card;
>> >> >> keep-power-in-suspend;
>> >> >>
>> >> >> #address-cells = <1>;
>> >> >> #size-cells = <0>;
>> >> >> wlcore: wlcore at 0 {
>> >> >> compatible = "ti,wl1835";
>> >> >> reg = <2>;
>> >> >> interrupt-parent = <&gpioa>;
>> >> >> interrupts = <8 IRQ_TYPE_EDGE_RISING>;
>> >> >> };
>> >> >> };
>> >> >>
>> >> >> >
>> >> >> > alex
>> >> >> >
>> >> >> >
>> >> >> >>
>> >> >> >>>
>> >> >> >>> Thanks in advance
>> >> >> >>> Alex
>> >> >> >>>
>> >> >> >>>
>> >> >> >>>>
>> >> >> >>>> Author: Bruno Herrera <bruherrera@gmail.com
>> <mailto:bruherrera@gmail.com>>
>>
>> >> >> >>>> Date: Sun Oct 16 14:50:00 2016 -0200
>> >> >> >>>>
>> >> >> >>>> ARM: DT: STM32: Use dma-ranges property per board not
>> at dtsi
>> >> >> >>>> file
>> >> >> >>>>
>> >> >> >>>> diff --git a/arch/arm/boot/dts/stm32429i-eval.dts
>> >> >> >>>> b/arch/arm/boot/dts/stm32429i-eval.dts
>> >> >> >>>> index 6bfc595..2a22a82 100644
>> >> >> >>>> --- a/arch/arm/boot/dts/stm32429i-eval.dts
>> >> >> >>>> +++ b/arch/arm/boot/dts/stm32429i-eval.dts
>> >> >> >>>> @@ -52,6 +52,10 @@
>> >> >> >>>> model = "STMicroelectronics STM32429i-EVAL board";
>> >> >> >>>> compatible = "st,stm32429i-eval", "st,stm32f429";
>> >> >> >>>>
>> >> >> >>>> + soc {
>> >> >> >>>> + dma-ranges = <0xC0000000 0x0 0x10000000>;
>> >> >> >>>> + };
>> >> >> >>>> +
>> >> >> >>>> chosen {
>> >> >> >>>> bootargs = "root=/dev/ram rdinit=/linuxrc";
>> >> >> >>>> stdout-path = "serial0:115200n8";
>> >> >> >>>> @@ -96,6 +100,7 @@
>> >> >> >>>>
>> >> >> >>>> ðernet0 {
>> >> >> >>>> status = "okay";
>> >> >> >>>> + dma-ranges;
>> >> >> >>>> pinctrl-0 = <ðernet0_mii>;
>> >> >> >>>> pinctrl-names = "default";
>> >> >> >>>> phy-mode = "mii-id";
>> >> >> >>>> @@ -116,6 +121,7 @@
>> >> >> >>>> };
>> >> >> >>>>
>> >> >> >>>> &usbotg_hs {
>> >> >> >>>> + dma-ranges;
>> >> >> >>>> dr_mode = "host";
>> >> >> >>>> phys = <&usbotg_hs_phy>;
>> >> >> >>>> phy-names = "usb2-phy";
>> >> >> >>>> diff --git a/arch/arm/boot/dts/stm32f429.dtsi
>> >> >> >>>> b/arch/arm/boot/dts/stm32f429.dtsi
>> >> >> >>>> index 7d624a2..697a133 100644
>> >> >> >>>> --- a/arch/arm/boot/dts/stm32f429.dtsi
>> >> >> >>>> +++ b/arch/arm/boot/dts/stm32f429.dtsi
>> >> >> >>>> @@ -59,7 +59,6 @@
>> >> >> >>>> };
>> >> >> >>>>
>> >> >> >>>> soc {
>> >> >> >>>> - dma-ranges = <0xc0000000 0x0 0x10000000>;
>> >> >> >>>>
>> >> >> >>>> timer2: timer at 40000000 {
>> >> >> >>>> compatible = "st,stm32-timer";
>> >> >> >>>> @@ -472,13 +471,11 @@
>> >> >> >>>> st,syscon = <&syscfg 0x4>;
>> >> >> >>>> snps,pbl = <8>;
>> >> >> >>>> snps,mixed-burst;
>> >> >> >>>> - dma-ranges;
>> >> >> >>>> status = "disabled";
>> >> >> >>>> };
>> >> >> >>>>
>> >> >> >>>> usbotg_hs: usb at 40040000 {
>> >> >> >>>> compatible = "snps,dwc2";
>> >> >> >>>> - dma-ranges;
>> >> >> >>>> reg = <0x40040000 0x40000>;
>> >> >> >>>> interrupts = <77>;
>> >> >> >>>> clocks = <&rcc 0 29>;
>> >> >> >>>>
>> >> >> >>>>
>> >> >> >>>>> diff --git a/arch/arm/boot/dts/stm32f429.dtsi
>> >> >> >>>>> b/arch/arm/boot/dts/stm32f429.dtsi
>> >> >> >>>>> index 0596d60..3a1cfdd 100644
>> >> >> >>>>> --- a/arch/arm/boot/dts/stm32f429.dtsi
>> >> >> >>>>> +++ b/arch/arm/boot/dts/stm32f429.dtsi
>> >> >> >>>>> @@ -59,8 +59,6 @@
>> >> >> >>>>> };
>> >> >> >>>>>
>> >> >> >>>>> soc {
>> >> >> >>>>> - dma-ranges = <0xc0000000 0x0 0x10000000>;
>> >> >> >>>>> -
>> >> >> >>>>> timer2: timer at 40000000 {
>> >> >> >>>>> compatible = "st,stm32-timer";
>> >> >> >>>>> reg = <0x40000000 0x400>;
>> >> >> >>>>> diff --git a/arch/arm/boot/dts/stm32f469-disco.dts
>> >> >> >>>>> b/arch/arm/boot/dts/stm32f469-disco.dts
>> >> >> >>>>> index 9e73656..c2213c0 100644
>> >> >> >>>>> --- a/arch/arm/boot/dts/stm32f469-disco.dts
>> >> >> >>>>> +++ b/arch/arm/boot/dts/stm32f469-disco.dts
>> >> >> >>>>> @@ -64,6 +64,10 @@
>> >> >> >>>>> aliases {
>> >> >> >>>>> serial0 = &usart3;
>> >> >> >>>>> };
>> >> >> >>>>> +
>> >> >> >>>>> + soc {
>> >> >> >>>>> + dma-ranges = <0xc0000000 0x0 0x10000000>;
>> >> >> >>>>> + };
>> >> >> >>>>> };
>> >> >> >>>>>
>> >> >> >>>>> &clk_hse {
>> >> >> >>>>> --
>> >> >> >>>>
>> >> >> >>>>
>> >> >> >>>>
>> >> >> >>>>
>> >> >> >>>> Br.,
>> >> >> >>>> Bruno
>> >> >> >>>>
>> >> >> >>>
>> >> >> >
>> >> >>
>> >> >> _______________________________________________
>> >> >> linux-arm-kernel mailing list
>> >> >> linux-arm-kernel at lists.infradead.org
>> <mailto:linux-arm-kernel@lists.infradead.org>
>> >> >> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>> <http://lists.infradead.org/mailman/listinfo/linux-arm-kernel>
>> >> >
>> >> >
>> >
>> >
>>
>>
>
^ permalink raw reply
* [PATCH v7 RESEND 2/2] ASoC: samsung: Add machine driver for Exynos5433 based TM2 board
From: Sylwester Nawrocki @ 2016-11-02 16:05 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds the sound machine driver for the TM2 and TM2E boards.
Speaker and headphone playback, Main Mic capture, Bluetooth, Voice
call and external accessory are supported.
Signed-off-by: Inha Song <ideal.song@samsung.com>
[k.kozlowski: rebased on 4.1]
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
[s.nawrocki: rebased to 4.7, adjustment to the ASoC core changes,
removed unused ops and direct calls to the max98504 function,
added parsing of "audio-amplifier" and "audio-codec"
properties, added TDM API calls, switched to gpiod API]
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Reviewed-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
---
Changes since v6:
- removed unused variables.
Changes since v5:
- dropped requesting and managing of the CODEC's clocks,
- removed driver remove() handler,
- changed pm_ops to use prepare/complete rather than
late_suspend/early_resume.
Changes since v4 (addressing review comments from Charles):
- changed the order of WM5110_FLL{1,2}, WM5110_FLL{1,2}_REFCLK setting,
- ARIZONA_CLK_SYSCLK, ARIZONA_CLK_ASYNCCLK setting moved to late_probe,
- added tm2_aif2_hw_free callback for disabling FLL2,
- removed unneded card->dapm.bias_level assignment in tm2_mic_bias callback,
- suspend_late, resume_early dev_pm_ops used instead of suspend_post,
resume_pre struct snd_soc_card callbacks.
Changes since v3:
- removed SND_SOC_SAMSUNG_AUDSS from Kconfig.
Changes since v2:
- added missing Kconfig dependencies.
Changes since initial version:
- added PDM Tx channels setup through TDM API
- adaptation to renamed 'samsung,model', 'samsung,i2s-controller',
'samsung,speaker-amplifier' properties,
- removed some dev_dbg() calls,
- cleaned up mic-bias GPIO handling and switched to gpiod API,
- added parsing of 'audio-codec' property,
- initialized codec_of_node of dai_link instead of codec_name,
- switched to using clock, clock-names properties from the wm5110
codec node,
- fixed error paths in probe() (of_node reference counting).
sound/soc/samsung/Kconfig | 9 +
sound/soc/samsung/Makefile | 2 +
sound/soc/samsung/tm2_wm5110.c | 552 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 563 insertions(+)
create mode 100644 sound/soc/samsung/tm2_wm5110.c
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index f6023b4..6b5b048 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -229,4 +229,13 @@ config SND_SOC_ARNDALE_RT5631_ALC5631
select SND_SAMSUNG_I2S
select SND_SOC_RT5631
+config SND_SOC_SAMSUNG_TM2_WM5110
+ tristate "SoC I2S Audio support for WM5110 on TM2 board"
+ depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER
+ select SND_SOC_MAX98504
+ select SND_SOC_WM5110
+ select SND_SAMSUNG_I2S
+ help
+ Say Y if you want to add support for SoC audio on the TM2 board.
+
endif #SND_SOC_SAMSUNG
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index 5d03f5c..4444b9f 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -44,6 +44,7 @@ snd-soc-lowland-objs := lowland.o
snd-soc-littlemill-objs := littlemill.o
snd-soc-bells-objs := bells.o
snd-soc-arndale-rt5631-objs := arndale_rt5631.o
+snd-soc-tm2-wm5110-objs := tm2_wm5110.o
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -69,3 +70,4 @@ obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o
obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o
obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o
obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o
+obj-$(CONFIG_SND_SOC_SAMSUNG_TM2_WM5110) += snd-soc-tm2-wm5110.o
diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c
new file mode 100644
index 0000000..5cdf7d1
--- /dev/null
+++ b/sound/soc/samsung/tm2_wm5110.c
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2015 - 2016 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Inha Song <ideal.song@samsung.com>
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "i2s.h"
+#include "../codecs/wm5110.h"
+
+/*
+ * The source clock is XCLKOUT with its mux set to the external fixed rate
+ * oscillator (XXTI).
+ */
+#define MCLK_RATE 24000000U
+
+#define TM2_DAI_AIF1 0
+#define TM2_DAI_AIF2 1
+
+struct tm2_machine_priv {
+ struct snd_soc_codec *codec;
+ unsigned int sysclk_rate;
+ struct gpio_desc *gpio_mic_bias;
+};
+
+static int tm2_start_sysclk(struct snd_soc_card *card)
+{
+ struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_codec *codec = priv->codec;
+ int ret;
+
+ ret = snd_soc_codec_set_pll(codec, WM5110_FLL1_REFCLK,
+ ARIZONA_FLL_SRC_MCLK1,
+ MCLK_RATE,
+ priv->sysclk_rate);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set FLL1 source: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_codec_set_pll(codec, WM5110_FLL1,
+ ARIZONA_FLL_SRC_MCLK1,
+ MCLK_RATE,
+ priv->sysclk_rate);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to start FLL1: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
+ ARIZONA_CLK_SRC_FLL1,
+ priv->sysclk_rate,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set SYSCLK source: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tm2_stop_sysclk(struct snd_soc_card *card)
+{
+ struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_codec *codec = priv->codec;
+ int ret;
+
+ ret = snd_soc_codec_set_pll(codec, WM5110_FLL1, 0, 0, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to stop FLL1: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
+ ARIZONA_CLK_SRC_FLL1, 0, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to stop SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tm2_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+
+ switch (params_rate(params)) {
+ case 4000:
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 96000:
+ case 192000:
+ /* Highest possible SYSCLK frequency: 147.456MHz */
+ priv->sysclk_rate = 147456000U;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ case 176400:
+ /* Highest possible SYSCLK frequency: 135.4752 MHz */
+ priv->sysclk_rate = 135475200U;
+ break;
+ default:
+ dev_err(codec->dev, "Not supported sample rate: %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ return tm2_start_sysclk(rtd->card);
+}
+
+static struct snd_soc_ops tm2_aif1_ops = {
+ .hw_params = tm2_aif1_hw_params,
+};
+
+static int tm2_aif2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ unsigned int asyncclk_rate;
+ int ret;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 12000:
+ case 16000:
+ /* Highest possible ASYNCCLK frequency: 49.152MHz */
+ asyncclk_rate = 49152000U;
+ break;
+ case 11025:
+ /* Highest possible ASYNCCLK frequency: 45.1584 MHz */
+ asyncclk_rate = 45158400U;
+ break;
+ default:
+ dev_err(codec->dev, "Not supported sample rate: %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ ret = snd_soc_codec_set_pll(codec, WM5110_FLL2_REFCLK,
+ ARIZONA_FLL_SRC_MCLK1,
+ MCLK_RATE,
+ asyncclk_rate);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set FLL2 source: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_codec_set_pll(codec, WM5110_FLL2,
+ ARIZONA_FLL_SRC_MCLK1,
+ MCLK_RATE,
+ asyncclk_rate);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to start FLL2: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK,
+ ARIZONA_CLK_SRC_FLL2,
+ asyncclk_rate,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set ASYNCCLK source: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tm2_aif2_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ int ret;
+
+ /* disable FLL2 */
+ ret = snd_soc_codec_set_pll(codec, WM5110_FLL2, ARIZONA_FLL_SRC_MCLK1,
+ 0, 0);
+ if (ret < 0)
+ dev_err(codec->dev, "Failed to stop FLL2: %d\n", ret);
+
+ return ret;
+}
+
+static struct snd_soc_ops tm2_aif2_ops = {
+ .hw_params = tm2_aif2_hw_params,
+ .hw_free = tm2_aif2_hw_free,
+};
+
+static int tm2_mic_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = w->dapm->card;
+ struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ gpiod_set_value_cansleep(priv->gpio_mic_bias, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ gpiod_set_value_cansleep(priv->gpio_mic_bias, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int tm2_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_pcm_runtime *rtd;
+
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+
+ if (dapm->dev != rtd->codec_dai->dev)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ if (card->dapm.bias_level == SND_SOC_BIAS_OFF)
+ tm2_start_sysclk(card);
+ break;
+ case SND_SOC_BIAS_OFF:
+ tm2_stop_sysclk(card);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_aux_dev tm2_speaker_amp_dev;
+
+static int tm2_late_probe(struct snd_soc_card *card)
+{
+ struct tm2_machine_priv *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai_link_component dlc = { 0 };
+ unsigned int ch_map[] = { 0, 1 };
+ struct snd_soc_dai *amp_pdm_dai;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *aif1_dai;
+ struct snd_soc_dai *aif2_dai;
+ int ret;
+
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[TM2_DAI_AIF1].name);
+ aif1_dai = rtd->codec_dai;
+ priv->codec = rtd->codec;
+
+ ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(aif1_dai->dev, "Failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[TM2_DAI_AIF2].name);
+ aif2_dai = rtd->codec_dai;
+
+ ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(aif2_dai->dev, "Failed to set ASYNCCLK: %d\n", ret);
+ return ret;
+ }
+
+ dlc.of_node = tm2_speaker_amp_dev.codec_of_node;
+ amp_pdm_dai = snd_soc_find_dai(&dlc);
+ if (!amp_pdm_dai)
+ return -ENODEV;
+
+ /* Set the MAX98504 V/I sense PDM Tx DAI channel mapping */
+ ret = snd_soc_dai_set_channel_map(amp_pdm_dai, ARRAY_SIZE(ch_map),
+ ch_map, 0, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_tdm_slot(amp_pdm_dai, 0x3, 0x0, 2, 16);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new tm2_controls[] = {
+ SOC_DAPM_PIN_SWITCH("HP"),
+ SOC_DAPM_PIN_SWITCH("SPK"),
+ SOC_DAPM_PIN_SWITCH("RCV"),
+ SOC_DAPM_PIN_SWITCH("VPS"),
+ SOC_DAPM_PIN_SWITCH("HDMI"),
+
+ SOC_DAPM_PIN_SWITCH("Main Mic"),
+ SOC_DAPM_PIN_SWITCH("Sub Mic"),
+ SOC_DAPM_PIN_SWITCH("Third Mic"),
+
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+const struct snd_soc_dapm_widget tm2_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("HP", NULL),
+ SND_SOC_DAPM_SPK("SPK", NULL),
+ SND_SOC_DAPM_SPK("RCV", NULL),
+ SND_SOC_DAPM_LINE("VPS", NULL),
+ SND_SOC_DAPM_LINE("HDMI", NULL),
+
+ SND_SOC_DAPM_MIC("Main Mic", tm2_mic_bias),
+ SND_SOC_DAPM_MIC("Sub Mic", NULL),
+ SND_SOC_DAPM_MIC("Third Mic", NULL),
+
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_component_driver tm2_component = {
+ .name = "tm2-audio",
+};
+
+static struct snd_soc_dai_driver tm2_ext_dai[] = {
+ {
+ .name = "Voice call",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+ {
+ .name = "Bluetooth",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+};
+
+static struct snd_soc_dai_link tm2_dai_links[] = {
+ {
+ .name = "WM5110 AIF1",
+ .stream_name = "HiFi Primary",
+ .codec_dai_name = "wm5110-aif1",
+ .ops = &tm2_aif1_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
+ }, {
+ .name = "WM5110 Voice",
+ .stream_name = "Voice call",
+ .codec_dai_name = "wm5110-aif2",
+ .ops = &tm2_aif2_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
+ .ignore_suspend = 1,
+ }, {
+ .name = "WM5110 BT",
+ .stream_name = "Bluetooth",
+ .codec_dai_name = "wm5110-aif3",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
+ .ignore_suspend = 1,
+ }
+};
+
+static struct snd_soc_card tm2_card = {
+ .owner = THIS_MODULE,
+
+ .dai_link = tm2_dai_links,
+ .num_links = ARRAY_SIZE(tm2_dai_links),
+ .controls = tm2_controls,
+ .num_controls = ARRAY_SIZE(tm2_controls),
+ .dapm_widgets = tm2_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tm2_dapm_widgets),
+ .aux_dev = &tm2_speaker_amp_dev,
+ .num_aux_devs = 1,
+
+ .late_probe = tm2_late_probe,
+ .set_bias_level = tm2_set_bias_level,
+};
+
+static int tm2_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card = &tm2_card;
+ struct tm2_machine_priv *priv;
+ struct device_node *cpu_dai_node, *codec_dai_node;
+ int ret, i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ snd_soc_card_set_drvdata(card, priv);
+ card->dev = dev;
+
+ priv->gpio_mic_bias = devm_gpiod_get(dev, "mic-bias",
+ GPIOF_OUT_INIT_LOW);
+ if (IS_ERR(priv->gpio_mic_bias)) {
+ dev_err(dev, "Failed to get mic bias gpio\n");
+ return PTR_ERR(priv->gpio_mic_bias);
+ }
+
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret < 0) {
+ dev_err(dev, "Card name is not specified\n");
+ return ret;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
+ if (ret < 0) {
+ dev_err(dev, "Audio routing is not specified or invalid\n");
+ return ret;
+ }
+
+ card->aux_dev[0].codec_of_node = of_parse_phandle(dev->of_node,
+ "audio-amplifier", 0);
+ if (!card->aux_dev[0].codec_of_node) {
+ dev_err(dev, "audio-amplifier property invalid or missing\n");
+ return -EINVAL;
+ }
+
+ cpu_dai_node = of_parse_phandle(dev->of_node, "i2s-controller", 0);
+ if (!cpu_dai_node) {
+ dev_err(dev, "i2s-controllers property invalid or missing\n");
+ ret = -EINVAL;
+ goto amp_node_put;
+ }
+
+ codec_dai_node = of_parse_phandle(dev->of_node, "audio-codec", 0);
+ if (!codec_dai_node) {
+ dev_err(dev, "audio-codec property invalid or missing\n");
+ ret = -EINVAL;
+ goto cpu_dai_node_put;
+ }
+
+ for (i = 0; i < card->num_links; i++) {
+ card->dai_link[i].cpu_dai_name = NULL;
+ card->dai_link[i].cpu_name = NULL;
+ card->dai_link[i].platform_name = NULL;
+ card->dai_link[i].codec_of_node = codec_dai_node;
+ card->dai_link[i].cpu_of_node = cpu_dai_node;
+ card->dai_link[i].platform_of_node = cpu_dai_node;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &tm2_component,
+ tm2_ext_dai, ARRAY_SIZE(tm2_ext_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register component: %d\n", ret);
+ goto codec_dai_node_put;
+ }
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register card: %d\n", ret);
+ goto codec_dai_node_put;
+ }
+
+codec_dai_node_put:
+ of_node_put(codec_dai_node);
+cpu_dai_node_put:
+ of_node_put(cpu_dai_node);
+amp_node_put:
+ of_node_put(card->aux_dev[0].codec_of_node);
+ return ret;
+}
+
+static int tm2_pm_prepare(struct device *dev)
+{
+ struct snd_soc_card *card = dev_get_drvdata(dev);
+
+ return tm2_stop_sysclk(card);
+}
+
+static void tm2_pm_complete(struct device *dev)
+{
+ struct snd_soc_card *card = dev_get_drvdata(dev);
+
+ tm2_start_sysclk(card);
+}
+
+const struct dev_pm_ops tm2_pm_ops = {
+ .prepare = tm2_pm_prepare,
+ .suspend = snd_soc_suspend,
+ .resume = snd_soc_resume,
+ .complete = tm2_pm_complete,
+ .freeze = snd_soc_suspend,
+ .thaw = snd_soc_resume,
+ .poweroff = snd_soc_poweroff,
+ .restore = snd_soc_resume,
+};
+
+static const struct of_device_id tm2_of_match[] = {
+ { .compatible = "samsung,tm2-audio" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tm2_of_match);
+
+static struct platform_driver tm2_driver = {
+ .driver = {
+ .name = "tm2-audio",
+ .pm = &tm2_pm_ops,
+ .of_match_table = tm2_of_match,
+ },
+ .probe = tm2_probe,
+};
+module_platform_driver(tm2_driver);
+
+MODULE_AUTHOR("Inha Song <ideal.song@samsung.com>");
+MODULE_DESCRIPTION("ALSA SoC Exynos TM2 Audio Support");
+MODULE_LICENSE("GPL v2");
--
1.9.1
^ permalink raw reply related
* [PATCH v3 2/3] Documentation: dt: add bindings for ti-cpufreq
From: Dave Gerlach @ 2016-11-02 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161102035941.GA10786@vireshk-i7>
Hi,
On 11/01/2016 10:59 PM, Viresh Kumar wrote:
> On 27-10-16, 16:41, Dave Gerlach wrote:
>> Add the device tree bindings document for the TI CPUFreq/OPP driver
>> on AM33xx and AM43xx SoCs. The operating-points-v2 binding allows us
>> to provide an opp-supported-hw property for each OPP to define when
>> it is available. This driver is responsible for reading and parsing
>> registers to determine which OPPs can be selectively enabled based
>> on the specific SoC in use by matching against the opp-supported-hw
>> data.
>>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> ---
>> v2->v3:
>> - Move ti,syscon-* properties under opp table instead of cpu node, as
>> that is a better location for them.
>> - For the opp table do not use platform specific compatible strings
>> but instead a operating-points-v2-ti-cpu
>>
>> .../devicetree/bindings/cpufreq/ti-cpufreq.txt | 132 +++++++++++++++++++++
>> 1 file changed, 132 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/cpufreq/ti-cpufreq.txt
>>
>> diff --git a/Documentation/devicetree/bindings/cpufreq/ti-cpufreq.txt b/Documentation/devicetree/bindings/cpufreq/ti-cpufreq.txt
>> new file mode 100644
>> index 000000000000..467ad29c75c9
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/cpufreq/ti-cpufreq.txt
>> @@ -0,0 +1,132 @@
>> +TI CPUFreq and OPP bindings
>> +================================
>> +
>> +Certain TI SoCs, like those in the am335x, am437x, am57xx, and dra7xx
>> +families support different OPPs depending on the silicon variant in use.
>> +The ti_cpufreq driver can use revision and an efuse value from the SoC to
>> +provide the OPP framework with supported hardware information. This is
>> +used to determine which OPPs from the operating-points-v2 table get enabled
>> +when it is parsed by the OPP framework.
>> +
>> +Required properties:
>> +--------------------
>> +In 'cpus' nodes:
>> +- operating-points-v2: Phandle to the operating-points-v2 table to use.
>> +
>> +In 'operating-points-v2' table:
>> +- compatible: Should be 'operating-points-v2-ti-cpu' for am335x, am43xx,
>> + and dra7xx/am57xx SoCs
>> +- ti,syscon-efuse: Syscon phandle, offset to efuse register, efuse register
>> + mask, and efuse register shift to get the relevant bits
>> + that describe OPP availability.
>> +- ti,syscon-rev: Syscon and offset used to look up revision value on SoC.
>> +
>> +Optional properties:
>> +--------------------
>> +For each opp entry in 'operating-points-v2' table:
>> +- opp-supported-hw: Two bitfields indicating:
>> + 1. Which revision of the SoC the OPP is supported by
>> + 2. Which eFuse bits indicate this OPP is available
>> +
>> + A bitwise AND is performed against these values and if any bit
>> + matches, the OPP gets enabled. Not providing the property for an
>> + entry indicates that an OPP is always supported.
>> +
>> +Example:
>> +--------
>> +
>> +/* From arch/arm/boot/dts/am33xx.dtsi */
>> +cpus {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + cpu at 0 {
>> + compatible = "arm,cortex-a8";
>> + device_type = "cpu";
>> + reg = <0>;
>> +
>> + operating-points-v2 = <&cpu0_opp_table>;
>> +
>> + clocks = <&dpll_mpu_ck>;
>> + clock-names = "cpu";
>> +
>> + clock-latency = <300000>; /* From omap-cpufreq driver */
>> + };
>> +};
>> +
>> +/*
>> + * cpu0 has different OPPs depending on SoC revision and some on revisions
>> + * 0x2 and 0x4 have eFuse bits that indicate if they are available or not
>> + */
>> +cpu0_opp_table: opp_table0 {
>> + compatible = "operating-points-v2-ti-am3352-cpu";
>> + ti,syscon-efuse = <&scm_conf 0x7fc 0x1fff 0>;
>> + ti,syscon-rev = <&scm_conf 0x600>;
>> +
>> + /*
>> + * The three following nodes are marked with opp-suspend
>> + * because they can not be enabled simultaneously on a
>> + * single SoC.
>> + */
>> + opp50 at 300000000 {
>> + opp-hz = /bits/ 64 <300000000>;
>> + opp-microvolt = <950000 931000 969000>;
>> + opp-supported-hw = <0x06 0x0010>;
>> + opp-suspend;
>> + };
>> +
>> + opp100 at 275000000 {
>> + opp-hz = /bits/ 64 <275000000>;
>> + opp-microvolt = <1100000 1078000 1122000>;
>> + opp-supported-hw = <0x01 0x00FF>;
>> + opp-suspend;
>> + };
>> +
>> + opp100 at 300000000 {
>> + opp-hz = /bits/ 64 <300000000>;
>> + opp-microvolt = <1100000 1078000 1122000>;
>> + opp-supported-hw = <0x06 0x0020>;
>> + opp-suspend;
>
> Only one OPP in the table can be marked as suspend OPP.
>
Does that still apply when opp-supported-hw is involved? Based on the
comment at the start of the table, those OPPs are all mutually exclusive
and will not ever be enabled on the same piece of silicon, they
represent the lowest OPP for each of three different supported-hw
configurations.
Regards,
Dave
^ permalink raw reply
* [PATCH v7 RESEND 1/2] ASoC: samsung: Add DT bindings documentation for TM2 sound subsystem
From: Sylwester Nawrocki @ 2016-11-02 16:02 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds DT binding documentation for Exnos5433 based TM2
and TM2E boards sound subsystem.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v5:
- none.
Changes since v4:
- indentation changes.
Changes since v2:
- none.
Changes since initial version:
- dropped clocks, clock-names properties, instead properties from
the CODEC node will be used,
- property renames: 'samsung,model' -> 'model', 'samsung,i2s-controller'
-> 'i2s-controller', 'samsung,speaker-amplifier' -> 'audio-amplifier',
- added 'audio-codec' property.
---
.../bindings/sound/samsung,tm2-audio.txt | 38 ++++++++++++++++++++++
1 file changed, 38 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/samsung,tm2-audio.txt
diff --git a/Documentation/devicetree/bindings/sound/samsung,tm2-audio.txt b/Documentation/devicetree/bindings/sound/samsung,tm2-audio.txt
new file mode 100644
index 0000000..94442e5
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/samsung,tm2-audio.txt
@@ -0,0 +1,38 @@
+Samsung Exynos5433 TM2(E) audio complex with WM5110 codec
+
+Required properties:
+
+ - compatible : "samsung,tm2-audio"
+ - model : the user-visible name of this sound complex
+ - audio-codec : the phandle of the wm5110 audio codec node,
+ as described in ../mfd/arizona.txt
+ - i2s-controller : the phandle of the I2S controller
+ - audio-amplifier : the phandle of the MAX98504 amplifier
+ - samsung,audio-routing : a list of the connections between audio components;
+ each entry is a pair of strings, the first being the
+ connection's sink, the second being the connection's
+ source; valid names for sources and sinks are the
+ WM5110's and MAX98504's pins and the jacks on the
+ board: HP, SPK, Main Mic, Sub Mic, Third Mic,
+ Headset Mic
+ - mic-bias-gpios : GPIO pin that enables the Main Mic bias regulator
+
+
+Example:
+
+sound {
+ compatible = "samsung,tm2-audio";
+ audio-codec = <&wm5110>;
+ i2s-controller = <&i2s0>;
+ audio-amplifier = <&max98504>;
+ mic-bias-gpios = <&gpr3 2 0>;
+ model = "wm5110";
+ samsung,audio-routing =
+ "HP", "HPOUT1L",
+ "HP", "HPOUT1R",
+ "SPK", "SPKOUT",
+ "SPKOUT", "HPOUT2L",
+ "SPKOUT", "HPOUT2R",
+ "Main Mic", "MICBIAS2",
+ "IN1R", "Main Mic";
+};
--
1.9.1
^ permalink raw reply related
* [PATCH v1 11/11] dts: arm64: hip06: Add Hisilicon SoC PMU support
From: Anurup M @ 2016-11-02 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478101374-18778-1-git-send-email-anurup.m@huawei.com>
1. Add nodes for hip06 L3 cache to support uncore events.
2. Add nodes for hip06 MN to support uncore events.
3. Add nodes for hip06 DDRC to support uncore events.
Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
arch/arm64/boot/dts/hisilicon/hip06.dtsi | 116 +++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index cb9e018..9ff3afe 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -980,6 +980,122 @@
status = "disabled";
};
+ djtag0: djtag at 60010000 {
+ compatible = "hisilicon,hip06-cpu-djtag-v1";
+ reg = <0x0 0x60010000 0x0 0x10000>;
+
+ /* L3 cache for socket0 CPU die scl#2 */
+ pmul3c0 {
+ compatible = "hisilicon,hisi-pmu-l3c-v1";
+ scl-id = <0x02>;
+ num-events = <0x16>;
+ num-counters = <0x08>;
+ module-id = <0x04 0x04 0x04 0x04>;
+ num-banks = <0x04>;
+ cfgen-map = <0x02 0x04 0x01 0x08>;
+ counter-reg = <0x170>;
+ evctrl-reg = <0x04>;
+ event-en = <0x1000000>;
+ evtype-reg = <0x140>;
+ };
+
+ /* Miscellaneous node for socket0
+ * CPU die scl#2
+ */
+ pmumn0 {
+ compatible = "hisilicon,hisi-pmu-mn-v1";
+ scl-id = <0x02>;
+ num-events = <0x09>;
+ num-counters = <0x04>;
+ module-id = <0x0b>;
+ cfgen-map = <0x01>;
+ counter-reg = <0x30>;
+ evctrl-reg = <0x40>;
+ event-en = <0x01>;
+ evtype-reg = <0x48>;
+ };
+ };
+
+ djtag1: djtag at 40010000 {
+ compatible = "hisilicon,hip06-cpu-djtag-v1";
+ reg = <0x0 0x40010000 0x0 0x10000>;
+
+ /* L3 cache for socket0 CPU die scl#1 */
+ pmul3c1 {
+ compatible = "hisilicon,hisi-pmu-l3c-v1";
+ scl-id = <0x01>;
+ num-events = <0x16>;
+ num-counters = <0x08>;
+ module-id = <0x04 0x04 0x04 0x04>;
+ num-banks = <0x04>;
+ cfgen-map = <0x02 0x04 0x01 0x08>;
+ counter-reg = <0x170>;
+ evctrl-reg = <0x04>;
+ event-en = <0x1000000>;
+ evtype-reg = <0x140>;
+ };
+
+ /* Miscellaneous node for socket0
+ * CPU die scl#1
+ */
+ pmumn1 {
+ compatible = "hisilicon,hisi-pmu-mn-v1";
+ scl-id = <0x01>;
+ num-events = <0x09>;
+ num-counters = <0x04>;
+ module-id = <0x0b>;
+ cfgen-map = <0x01>;
+ counter-reg = <0x30>;
+ evctrl-reg = <0x40>;
+ event-en = <0x01>;
+ evtype-reg = <0x48>;
+ };
+ };
+
+ /* DDRC for CPU die scl #1 Channel #0 */
+ pmu_sccl0_ddrc0: pmu_ddrc0 at 40348000 {
+ compatible = "hisilicon,hisi-pmu-ddrc-v1";
+ scl-id = <0x01>;
+ ch-id = <0x0>;
+ num-events = <0x0d>;
+ num-counters = <0x04>;
+ reg = <0x0 0x40348000 0x0 0x10000>; /* TOTEMA DDRC0 */
+ status = "okay";
+ };
+
+ /* DDRC for CPU die scl #1 Channel #1 */
+ pmu_sccl0_ddrc1: pmu_ddrc1 at 40358000 {
+ compatible = "hisilicon,hisi-pmu-ddrc-v1";
+ scl-id = <0x01>;
+ ch-id = <0x01>;
+ num-events = <0x0d>;
+ num-counters = <0x04>;
+ reg = <0x0 0x40358000 0x0 0x10000>; /* TOTEMA DDRC1 */
+ status = "okay";
+ };
+
+ /* DDRC for CPU die scl #2 Channel #0 */
+ pmu_sccl1_ddrc0: pmu_ddrc0 at 60348000 {
+ compatible = "hisilicon,hisi-pmu-ddrc-v1";
+ scl-id = <0x02>;
+ ch-id = <0x0>;
+ num-events = <0x0d>;
+ num-counters = <0x04>;
+ reg = <0x0 0x60348000 0x0 0x10000>; /* TOTEMC DDRC0 */
+ status = "okay";
+ };
+
+ /* DDRC for CPU die scl #2 Channel #1 */
+ pmu_sccl1_ddrc1: pmu_ddrc1 at 60358000 {
+ compatible = "hisilicon,hisi-pmu-ddrc-v1";
+ scl-id = <0x02>;
+ ch-id = <0x01>;
+ num-events = <0x0d>;
+ num-counters = <0x04>;
+ reg = <0x0 0x60358000 0x0 0x10000>; /* TOTEMC DDRC1 */
+ status = "okay";
+ };
+
sas1: sas at a2000000 {
compatible = "hisilicon,hip06-sas-v2";
reg = <0 0xa2000000 0 0x10000>;
--
2.1.4
^ permalink raw reply related
* [PATCH v1 10/11] perf: hisi: Support for Hisilicon DDRC PMU.
From: Anurup M @ 2016-11-02 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478101374-18778-1-git-send-email-anurup.m@huawei.com>
1. Add support for counting Hisilicon DDRC
statistics events in perf.
2. Support a total of 13 statistics events.
3. Events listed in /sys/devices/<pmu_name>/
Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
---
drivers/perf/hisilicon/Makefile | 2 +-
drivers/perf/hisilicon/hisi_uncore_ddrc.c | 444 ++++++++++++++++++++++++++++++
drivers/perf/hisilicon/hisi_uncore_ddrc.h | 73 +++++
3 files changed, 518 insertions(+), 1 deletion(-)
create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.c
create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.h
diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
index 8975104..8e9df2e 100644
--- a/drivers/perf/hisilicon/Makefile
+++ b/drivers/perf/hisilicon/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o hisi_uncore_mn.o
+obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o hisi_uncore_mn.o hisi_uncore_ddrc.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc.c b/drivers/perf/hisilicon/hisi_uncore_ddrc.c
new file mode 100644
index 0000000..b89a72e
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_ddrc.c
@@ -0,0 +1,444 @@
+/*
+ * HiSilicon SoC DDRC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/bitmap.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_ddrc.h"
+
+static inline int hisi_ddrc_counter_valid(int idx, struct hisi_pmu *ddrc_pmu)
+{
+ return (idx >= 0 && idx < ddrc_pmu->num_counters);
+}
+
+static u32 hisi_ddrc_read32_relaxed(void __iomem *regs_base, u32 off)
+{
+ void __iomem *reg_addr = regs_base + off;
+
+ return readl_relaxed(reg_addr);
+}
+
+static void hisi_ddrc_write32(void __iomem *regs_base, u32 off, u32 val)
+{
+ void __iomem *reg_addr = regs_base + off;
+
+ writel(val, reg_addr);
+}
+
+static u32 hisi_read_ddrc_counter(struct hisi_ddrc_data *ddrc_hwmod_data,
+ unsigned long event_code, int idx)
+{
+ u32 value;
+ u32 reg_off;
+
+ reg_off = HISI_DDRC_FLUX_WR_REG_OFF + (event_code * 4);
+
+ value = hisi_ddrc_read32_relaxed(ddrc_hwmod_data->regs_base,
+ reg_off);
+ return value;
+}
+
+static u64 hisi_ddrc_event_update(struct perf_event *event,
+ struct hw_perf_event *hwc, int idx)
+{
+ struct hisi_pmu *ddrc_pmu = to_hisi_pmu(event->pmu);
+ struct hisi_ddrc_data *ddrc_hwmod_data;
+ u64 delta, prev_raw_count, new_raw_count = 0;
+
+ if (!hisi_ddrc_counter_valid(idx, ddrc_pmu)) {
+ dev_err(ddrc_pmu->dev,
+ "%s: Unsupported event index:%d!\n", __func__, idx);
+ return 0;
+ }
+
+ ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+
+ /* Check if the DDRC data is initialized for this SCCL */
+ if (!ddrc_hwmod_data->regs_base) {
+ dev_err(ddrc_pmu->dev, "DDRC registers not mapped!\n");
+ return 0;
+ }
+
+ do {
+ prev_raw_count = local64_read(&hwc->prev_count);
+ new_raw_count =
+ hisi_read_ddrc_counter(ddrc_hwmod_data,
+ hwc->config_base, idx);
+ delta = (new_raw_count - prev_raw_count) &
+ HISI_MAX_PERIOD;
+
+ local64_add(delta, &event->count);
+ } while (local64_cmpxchg(
+ &hwc->prev_count, prev_raw_count, new_raw_count) !=
+ prev_raw_count);
+
+ return new_raw_count;
+}
+
+static u32 hisi_write_ddrc_counter(struct hisi_pmu *ddrc_pmu,
+ struct hw_perf_event *hwc, u32 value)
+{
+ struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+ u32 reg_off;
+ u32 event_code = hwc->config_base;
+
+ if (!(event_code >= HISI_HWEVENT_DDRC_FLUX_WR &&
+ event_code < HISI_HWEVENT_DDRC_MAX_EVENT)) {
+ dev_err(ddrc_pmu->dev, "Unknown DDR evevnt!");
+ return 0;
+ }
+
+ if (!ddrc_hwmod_data->regs_base) {
+ dev_err(ddrc_pmu->dev,
+ "DDR reg address not mapped!\n");
+ return 0;
+ }
+
+ reg_off = HISI_DDRC_FLUX_WR_REG_OFF + (event_code * 4);
+
+ hisi_ddrc_write32(ddrc_hwmod_data->regs_base,
+ reg_off, value);
+
+ return value;
+}
+
+static void hisi_ddrc_set_event_period(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hisi_pmu *ddrc_pmu = to_hisi_pmu(event->pmu);
+ struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+ u32 event_code = hwc->config_base;
+ u32 reg_off;
+ u32 value;
+
+ reg_off = HISI_DDRC_FLUX_WR_REG_OFF + (event_code * 4);
+ /*
+ * For Hisilicon DDRC PMU we save the current counter value
+ * to prev_count, as we have enabled continuous counting for
+ * DDRC.
+ */
+ value = hisi_ddrc_read32_relaxed(ddrc_hwmod_data->regs_base,
+ reg_off);
+ local64_set(&hwc->prev_count, value);
+}
+
+static void hisi_clear_ddrc_event_idx(struct hisi_pmu *ddrc_pmu, int idx)
+{
+ struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+ void *bitmap_addr;
+
+ if (!hisi_ddrc_counter_valid(idx, ddrc_pmu)) {
+ dev_err(ddrc_pmu->dev,
+ "%s:Unsupported event index:%d!\n", __func__, idx);
+ return;
+ }
+
+ bitmap_addr = ddrc_hwmod_data->hisi_ddrc_event_used_mask;
+
+ __clear_bit(idx, bitmap_addr);
+}
+
+static int hisi_ddrc_get_event_idx(struct hisi_pmu *ddrc_pmu)
+{
+ struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+ int event_idx;
+
+ event_idx =
+ find_first_zero_bit(
+ ddrc_hwmod_data->hisi_ddrc_event_used_mask,
+ ddrc_pmu->num_counters);
+
+ if (event_idx == ddrc_pmu->num_counters)
+ return -EAGAIN;
+
+ __set_bit(event_idx,
+ ddrc_hwmod_data->hisi_ddrc_event_used_mask);
+
+ return event_idx;
+}
+
+static void hisi_free_ddrc_data(struct hisi_pmu *ddrc_pmu)
+{
+ kfree(ddrc_pmu->hwmod_data);
+ ddrc_pmu->hwmod_data = NULL;
+}
+
+static void init_hisi_ddr(void __iomem *reg_base)
+{
+ u32 value;
+
+ hisi_ddrc_write32(reg_base, HISI_DDRC_CTRL_PERF_REG_OFF, 0);
+
+ value = hisi_ddrc_read32_relaxed(reg_base, HISI_DDRC_CFG_PERF_REG_OFF);
+ value &= 0x2fffffff;
+ hisi_ddrc_write32(reg_base, HISI_DDRC_CFG_PERF_REG_OFF, value);
+
+ /* Enable Continuous counting */
+ hisi_ddrc_write32(reg_base, HISI_DDRC_CTRL_PERF_REG_OFF, 1);
+}
+
+static int init_hisi_ddrc_dts_data(struct platform_device *pdev,
+ struct hisi_ddrc_data *ddrc_hwmod_data)
+{
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ /* Continue for zero entries */
+ if (!res) {
+ dev_err(&pdev->dev, "No DDR reg resorces!\n");
+ return -EINVAL;
+ }
+
+ if (!resource_size(res)) {
+ dev_err(&pdev->dev, "Zero DDR reg entry!\n");
+ return -EINVAL;
+ }
+
+ ddrc_hwmod_data->regs_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ddrc_hwmod_data->regs_base))
+ return PTR_ERR(ddrc_hwmod_data->regs_base);
+
+ init_hisi_ddr(ddrc_hwmod_data->regs_base);
+
+ return 0;
+}
+
+static int init_hisi_ddrc_data(struct platform_device *pdev,
+ struct hisi_pmu *ddrc_pmu)
+{
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ddrc_pmu->hw_events.events = devm_kcalloc(dev,
+ ddrc_pmu->num_counters,
+ sizeof(*ddrc_pmu->hw_events.events),
+ GFP_KERNEL);
+ if (!ddrc_pmu->hw_events.events) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ raw_spin_lock_init(&ddrc_pmu->hw_events.pmu_lock);
+
+ init_hisi_ddrc_dts_data(pdev, ddrc_pmu->hwmod_data);
+
+ return 0;
+
+fail:
+ hisi_free_ddrc_data(ddrc_pmu);
+ return ret;
+}
+
+static struct attribute *hisi_ddrc_format_attr[] = {
+ HISI_PMU_FORMAT_ATTR(event, "config:0-11"),
+ NULL,
+};
+
+static struct attribute_group hisi_ddrc_format_group = {
+ .name = "format",
+ .attrs = hisi_ddrc_format_attr,
+};
+
+static struct attribute *hisi_ddrc_events_attr[] = {
+ HISI_PMU_EVENT_ATTR_STR(flux_write, "event=0x00"),
+ HISI_PMU_EVENT_ATTR_STR(flux_read, "event=0x01"),
+ HISI_PMU_EVENT_ATTR_STR(flux_write_cmd, "event=0x02"),
+ HISI_PMU_EVENT_ATTR_STR(flux_read_cmd, "event=0x03"),
+ HISI_PMU_EVENT_ATTR_STR(fluxid_write, "event=0x04"),
+ HISI_PMU_EVENT_ATTR_STR(fluxid_read, "event=0x05"),
+ HISI_PMU_EVENT_ATTR_STR(fluxid_write_cmd, "event=0x06"),
+ HISI_PMU_EVENT_ATTR_STR(fluxid_read_cmd, "event=0x07"),
+ HISI_PMU_EVENT_ATTR_STR(write_latency_cnt0,
+ "event=0x08"),
+ HISI_PMU_EVENT_ATTR_STR(read_latency_cnt0,
+ "event=0x09"),
+ HISI_PMU_EVENT_ATTR_STR(write_latency_cnt1,
+ "event=0x0A"),
+ HISI_PMU_EVENT_ATTR_STR(read_latency_cnt1,
+ "event=0x0B"),
+ HISI_PMU_EVENT_ATTR_STR(read_latency_cnt_inher,
+ "event=0x0C"),
+ NULL,
+};
+
+static struct attribute_group hisi_ddrc_events_group = {
+ .name = "events",
+ .attrs = hisi_ddrc_events_attr,
+};
+
+static struct attribute *hisi_ddrc_attrs[] = {
+ NULL,
+};
+
+struct attribute_group hisi_ddrc_attr_group = {
+ .attrs = hisi_ddrc_attrs,
+};
+
+static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
+
+static struct attribute *hisi_ddrc_cpumask_attrs[] = {
+ &dev_attr_cpumask.attr,
+ NULL,
+};
+
+static const struct attribute_group hisi_ddrc_cpumask_attr_group = {
+ .attrs = hisi_ddrc_cpumask_attrs,
+};
+
+static const struct attribute_group *hisi_ddrc_pmu_attr_groups[] = {
+ &hisi_ddrc_attr_group,
+ &hisi_ddrc_format_group,
+ &hisi_ddrc_events_group,
+ &hisi_ddrc_cpumask_attr_group,
+ NULL,
+};
+
+static struct hisi_uncore_ops hisi_uncore_ddrc_ops = {
+ .set_event_period = hisi_ddrc_set_event_period,
+ .get_event_idx = hisi_ddrc_get_event_idx,
+ .clear_event_idx = hisi_clear_ddrc_event_idx,
+ .event_update = hisi_ddrc_event_update,
+ .write_counter = hisi_write_ddrc_counter,
+};
+
+static int hisi_ddrc_pmu_init(struct device *dev,
+ struct hisi_pmu *ddrc_pmu)
+{
+ struct hisi_ddrc_data *ddrc_hwmod_data;
+ struct hisi_ddrc_hwcfg *ddrc_hwcfg;
+ int ret;
+
+ /* Read common PMU properties */
+ ret = hisi_uncore_common_fwprop_read(dev, ddrc_pmu);
+ if (ret)
+ return ret;
+
+ ddrc_hwmod_data = kzalloc(sizeof(struct hisi_ddrc_data),
+ GFP_KERNEL);
+ if (!ddrc_hwmod_data)
+ return -ENOMEM;
+
+ ddrc_hwcfg = &ddrc_hwmod_data->ddrc_hwcfg;
+ if (of_property_read_u32(dev->of_node, "ch-id",
+ &ddrc_hwcfg->channel_id)) {
+ kfree(ddrc_hwmod_data);
+ return -EINVAL;
+ }
+
+ ddrc_pmu->name = kasprintf(GFP_KERNEL, "hisi_ddrc%d_%d",
+ ddrc_pmu->scl_id, ddrc_hwcfg->channel_id);
+
+ ddrc_pmu->ops = &hisi_uncore_ddrc_ops;
+ ddrc_pmu->hwmod_data = ddrc_hwmod_data;
+ ddrc_pmu->dev = dev;
+
+ /* Pick one core to use for cpumask attributes */
+ cpumask_set_cpu(smp_processor_id(), &ddrc_pmu->cpu);
+
+ return 0;
+}
+
+static const struct of_device_id ddrc_of_match[] = {
+ { .compatible = "hisilicon,hisi-pmu-ddrc-v1", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ddrc_of_match);
+
+static int hisi_pmu_ddrc_dev_probe(struct platform_device *pdev)
+{
+ struct hisi_pmu *ddrc_pmu;
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id;
+ int ret;
+
+ of_id = of_match_device(ddrc_of_match, dev);
+ if (!of_id)
+ return -EINVAL;
+
+ ddrc_pmu = hisi_pmu_alloc(dev);
+ if (IS_ERR(ddrc_pmu))
+ return PTR_ERR(ddrc_pmu);
+
+ ret = hisi_ddrc_pmu_init(dev, ddrc_pmu);
+ if (ret)
+ return ret;
+
+ ret = init_hisi_ddrc_data(pdev, ddrc_pmu);
+ if (ret)
+ goto fail_init;
+
+ /* Register with perf PMU */
+ ddrc_pmu->pmu = (struct pmu) {
+ .name = ddrc_pmu->name,
+ .task_ctx_nr = perf_invalid_context,
+ .event_init = hisi_uncore_pmu_event_init,
+ .add = hisi_uncore_pmu_add,
+ .del = hisi_uncore_pmu_del,
+ .start = hisi_uncore_pmu_start,
+ .stop = hisi_uncore_pmu_stop,
+ .read = hisi_uncore_pmu_read,
+ .attr_groups = hisi_ddrc_pmu_attr_groups,
+ };
+
+ ret = hisi_uncore_pmu_setup(ddrc_pmu, ddrc_pmu->name);
+ if (ret) {
+ dev_err(ddrc_pmu->dev, "hisi_uncore_pmu_init FAILED!!\n");
+ goto fail;
+ }
+
+ platform_set_drvdata(pdev, ddrc_pmu);
+
+ return 0;
+fail:
+ hisi_free_ddrc_data(ddrc_pmu);
+
+fail_init:
+ dev_err(ddrc_pmu->dev, "%s failed\n", __func__);
+
+ return ret;
+}
+
+static int hisi_pmu_ddrc_dev_remove(struct platform_device *pdev)
+{
+ struct hisi_pmu *ddrc_pmu = platform_get_drvdata(pdev);
+
+ perf_pmu_unregister(&ddrc_pmu->pmu);
+ hisi_free_ddrc_data(ddrc_pmu);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver hisi_pmu_ddrc_driver = {
+ .driver = {
+ .name = "hisi-pmu-ddrc",
+ .of_match_table = ddrc_of_match,
+ },
+ .probe = hisi_pmu_ddrc_dev_probe,
+ .remove = hisi_pmu_ddrc_dev_remove,
+};
+module_platform_driver(hisi_pmu_ddrc_driver);
+
+MODULE_DESCRIPTION("HiSilicon SoC HIP0x DDRC PMU driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Anurup M");
diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc.h b/drivers/perf/hisilicon/hisi_uncore_ddrc.h
new file mode 100644
index 0000000..89eab6b
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_ddrc.h
@@ -0,0 +1,73 @@
+/*
+ * HiSilicon SoC DDRC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __HISI_UNCORE_DDRC_H__
+#define __HISI_UNCORE_DDRC_H__
+
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon DDRC event types.
+ */
+enum armv8_hisi_ddrc_event_types {
+ HISI_HWEVENT_DDRC_FLUX_WR = 0x0,
+ HISI_HWEVENT_DDRC_FLUX_RD = 0x01,
+ HISI_HWEVENT_DDRC_FLUX_WCMD = 0x02,
+ HISI_HWEVENT_DDRC_FLUX_RCMD = 0x03,
+ HISI_HWEVENT_DDRC_FLUXID_WD = 0x04,
+ HISI_HWEVENT_DDRC_FLUXID_RD = 0x05,
+ HISI_HWEVENT_DDRC_FLUXID_WCMD = 0x06,
+ HISI_HWEVENT_DDRC_FLUXID_RCMD = 0x07,
+ HISI_HWEVENT_DDRC_WLAT_CNT0 = 0x08,
+ HISI_HWEVENT_DDRC_RLAT_CNT0 = 0x09,
+ HISI_HWEVENT_DDRC_WLAT_CNT1 = 0x0A,
+ HISI_HWEVENT_DDRC_RLAT_CNT1 = 0x0B,
+ HISI_HWEVENT_DDRC_INHERE_RLAT_CNT = 0x0C,
+ HISI_HWEVENT_DDRC_MAX_EVENT,
+};
+
+#define HISI_DDRC_CTRL_PERF_REG_OFF 0x010
+#define HISI_DDRC_CFG_PERF_REG_OFF 0x270
+#define HISI_DDRC_FLUX_WR_REG_OFF 0x380
+#define HISI_DDRC_FLUX_RD_REG_OFF 0x384
+#define HISI_DDRC_FLUX_WCMD_REG_OFF 0x388
+#define HISI_DDRC_FLUX_RCMD_REG_OFF 0x38C
+#define HISI_DDRC_FLUXID_WR_REG_OFF 0x390
+#define HISI_DDRC_FLUXID_RD_REG_OFF 0x394
+#define HISI_DDRC_FLUXID_WCMD_REG_OFF 0x398
+#define HISI_DDRC_FLUXID_RCMD_REG_OFF 0x39C
+#define HISI_DDRC_FLUX_WLATCNT0_REG_OFF 0x3A0
+#define HISI_DDRC_FLUX_RLAT_CNT0_REG_OFF 0x3A4
+#define HISI_DDRC_FLUX_WLATCNT1_REG_OFF 0x3A8
+#define HISI_DDRC_FLUX_RLAT_CNT1_REG_OFF 0x3AC
+
+struct hisi_ddrc_hwcfg {
+ u32 channel_id;
+};
+
+struct hisi_ddrc_data {
+ void __iomem *regs_base;
+ DECLARE_BITMAP(hisi_ddrc_event_used_mask,
+ HISI_HWEVENT_DDRC_MAX_EVENT);
+ struct hisi_ddrc_hwcfg ddrc_hwcfg;
+};
+
+#endif /* __HISI_UNCORE_DDRC_H__ */
--
2.1.4
^ permalink raw reply related
* [PATCH v1 09/11] perf: hisi: Miscellanous node(MN) event counting in perf
From: Anurup M @ 2016-11-02 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478101374-18778-1-git-send-email-anurup.m@huawei.com>
From: Shaokun Zhang <zhangshaokun@hisilicon.com>
1. Add support to count MN hardware events.
2. Mn events are listed in sysfs at /sys/devices/hisi_mn2/events/
The events can be selected as shown in perf list
e.g.: For MN_READ_REQUEST event for Super CPU cluster 2 the
event format is
-e "hisi_mn2/read_req/"
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: Anurup M <anurup.m@huawei.com>
---
drivers/perf/hisilicon/Makefile | 2 +-
drivers/perf/hisilicon/hisi_uncore_mn.c | 571 ++++++++++++++++++++++++++++++++
drivers/perf/hisilicon/hisi_uncore_mn.h | 68 ++++
3 files changed, 640 insertions(+), 1 deletion(-)
create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.c
create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.h
diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
index e1766cf..8975104 100644
--- a/drivers/perf/hisilicon/Makefile
+++ b/drivers/perf/hisilicon/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o
+obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o hisi_uncore_mn.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_mn.c b/drivers/perf/hisilicon/hisi_uncore_mn.c
new file mode 100644
index 0000000..920e346
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_mn.c
@@ -0,0 +1,571 @@
+/*
+ * HiSilicon SoC MN Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/bitmap.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_mn.h"
+
+static inline int hisi_mn_counter_valid(int idx)
+{
+ return (idx >= HISI_IDX_MN_COUNTER0 &&
+ idx <= HISI_IDX_MN_COUNTER_MAX);
+}
+
+static u32 hisi_read_mn_counter(struct hisi_mn_data *mn_hwmod_data,
+ int idx)
+{
+ u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+ struct hisi_djtag_client *client = mn_hwmod_data->client;
+ u32 cfg_en, reg_offset, value;
+
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+ reg_offset = mn_hwmod_data->mn_hwcfg.counter_reg0_off + (idx * 4);
+
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client, &value);
+
+ return value;
+}
+
+static u64 hisi_mn_event_update(struct perf_event *event,
+ struct hw_perf_event *hwc, int idx)
+{
+ struct hisi_pmu *pmn_pmu = to_hisi_pmu(event->pmu);
+ struct hisi_mn_data *mn_hwmod_data;
+ u64 delta, prev_raw_count, new_raw_count = 0;
+ u32 cfg_en;
+
+ if (!hisi_mn_counter_valid(idx)) {
+ dev_err(pmn_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return 0;
+ }
+
+ mn_hwmod_data = pmn_pmu->hwmod_data;
+
+ /* Check if the MN data is initialized for this SCCL */
+ if (!mn_hwmod_data->client) {
+ dev_err(pmn_pmu->dev,
+ "SCL=%d not initialized!\n", pmn_pmu->scl_id);
+ return 0;
+ }
+
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+ do {
+ prev_raw_count = local64_read(&hwc->prev_count);
+ new_raw_count =
+ hisi_read_mn_counter(mn_hwmod_data, idx);
+ delta = (new_raw_count - prev_raw_count) &
+ HISI_MAX_PERIOD;
+
+ local64_add(delta, &event->count);
+ } while (local64_cmpxchg(
+ &hwc->prev_count, prev_raw_count, new_raw_count) !=
+ prev_raw_count);
+
+ return new_raw_count;
+}
+
+static void hisi_set_mn_evtype(struct hisi_pmu *pmn_pmu, int idx, u32 val)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+ u32 reg_offset = mn_hwmod_data->mn_hwcfg.evtype_reg0_off;
+ u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+ u32 cfg_en, event_value, value = 0;
+
+ event_value = (val -
+ HISI_HWEVENT_MN_EO_BARR_REQ);
+
+ /* Value to write to event type register */
+ val = event_value << (8 * idx);
+
+ /* Find the djtag Identifier of the Unit */
+ client = mn_hwmod_data->client;
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+ /*
+ * Set the event in MN_EVENT_TYPE Register
+ */
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client, &value);
+
+ value &= ~(0xff << (8 * idx));
+ value |= val;
+
+ hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+}
+
+static u32 hisi_write_mn_counter(struct hisi_pmu *pmn_pmu,
+ struct hw_perf_event *hwc, u32 value)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+ u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+ u32 cfg_en, reg_offset;
+ int ret;
+ int idx = GET_CNTR_IDX(hwc);
+
+ if (!hisi_mn_counter_valid(idx)) {
+ dev_err(pmn_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return -EINVAL;
+ }
+
+ reg_offset = mn_hwmod_data->mn_hwcfg.counter_reg0_off +
+ (idx * 4);
+ /* Find the djtag Identifier of the Unit */
+ client = mn_hwmod_data->client;
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+ ret = hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+
+ return ret;
+}
+
+static void hisi_enable_mn_counter(struct hisi_pmu *pmn_pmu, int idx)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+ u32 reg_offset = mn_hwmod_data->mn_hwcfg.event_ctrl_reg_off;
+ u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+ u32 event_en = mn_hwmod_data->mn_hwcfg.event_enable;
+ u32 cfg_en, value;
+
+ if (!hisi_mn_counter_valid(idx)) {
+ dev_err(pmn_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return;
+ }
+
+ /* Find the djtag Identifier of the Unit */
+ client = mn_hwmod_data->client;
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+ /*
+ * Set the event_bus_en bit in MN_EVENT_CTRL to enable counting
+ */
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client,
+ &value);
+
+ value |= event_en;
+ hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+}
+
+static void hisi_disable_mn_counter(struct hisi_pmu *pmn_pmu, int idx)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+ u32 reg_offset = mn_hwmod_data->mn_hwcfg.event_ctrl_reg_off;
+ u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+ u32 event_en = mn_hwmod_data->mn_hwcfg.event_enable;
+ u32 cfg_en, value;
+
+ if (!hisi_mn_counter_valid(idx)) {
+ dev_err(pmn_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return;
+ }
+
+ /* Find the djtag Identifier of the Unit */
+ client = mn_hwmod_data->client;
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+ /*
+ * Clear the event_bus_en bit in MN event control
+ */
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client, &value);
+
+ value &= ~(event_en);
+ hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+}
+
+static void hisi_clear_mn_event_idx(struct hisi_pmu *pmn_pmu, int idx)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+ u32 reg_offset = mn_hwmod_data->mn_hwcfg.evtype_reg0_off;
+ u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+ void *bitmap_addr;
+ u32 cfg_en, value;
+
+ if (!hisi_mn_counter_valid(idx)) {
+ dev_err(pmn_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return;
+ }
+
+ bitmap_addr = mn_hwmod_data->hisi_mn_event_used_mask;
+
+ __clear_bit(idx, bitmap_addr);
+
+ /* Find the djtag Identifier of the Unit */
+ client = mn_hwmod_data->client;
+ cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+ /*
+ * Clear the event in MN_EVENT_TYPE Register
+ */
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client, &value);
+
+ value &= ~(0xff << (8 * idx));
+ value |= (0xff << (8 * idx));
+ hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+}
+
+static int hisi_mn_get_event_idx(struct hisi_pmu *pmn_pmu)
+{
+ struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+ int event_idx;
+
+ event_idx =
+ find_first_zero_bit(
+ mn_hwmod_data->hisi_mn_event_used_mask,
+ HISI_MAX_CFG_MN_CNTR);
+
+ if (event_idx == HISI_MAX_CFG_MN_CNTR)
+ return -EAGAIN;
+
+ __set_bit(event_idx,
+ mn_hwmod_data->hisi_mn_event_used_mask);
+
+ return event_idx;
+}
+
+static void hisi_free_mn_data(struct hisi_pmu *pmn_pmu)
+{
+ kfree(pmn_pmu->hwmod_data);
+ pmn_pmu->hwmod_data = NULL;
+}
+
+static int init_hisi_mn_hwcfg(struct device *dev,
+ struct hisi_mn_data *pmn_data)
+{
+ struct hisi_mn_hwcfg *pmn_hwcfg = &pmn_data->mn_hwcfg;
+ struct device_node *node = dev->of_node;
+
+ if (of_property_read_u32(node, "counter-reg",
+ &pmn_hwcfg->counter_reg0_off)) {
+ dev_err(dev, "DT:Couldnot read counter-reg!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "evctrl-reg",
+ &pmn_hwcfg->event_ctrl_reg_off)) {
+ dev_err(dev, "DT:Couldnot read evctrl-reg!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "event-en",
+ &pmn_hwcfg->event_enable)) {
+ dev_err(dev, "DT:Couldnot read event-en property!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "evtype-reg",
+ &pmn_hwcfg->evtype_reg0_off)) {
+ dev_err(dev, "DT:Couldnot read evtype-reg!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "module-id",
+ &pmn_hwcfg->module_id)) {
+ dev_err(dev, "DT:Couldnot read module-id property!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "cfgen-map",
+ &pmn_hwcfg->bank_cfgen)) {
+ dev_err(dev, "DT:Couldnot read cfgen-map property!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int init_hisi_mn_data(struct device *dev,
+ struct hisi_pmu *pmn_pmu,
+ struct hisi_djtag_client *client)
+{
+ struct hisi_mn_data *mn_hwmod_data;
+ int ret;
+
+ mn_hwmod_data = kzalloc(sizeof(struct hisi_mn_data),
+ GFP_KERNEL);
+ if (!mn_hwmod_data)
+ return -ENOMEM;
+
+ /* Set the djtag Identifier */
+ mn_hwmod_data->client = client;
+
+ pmn_pmu->hw_events.events = devm_kcalloc(dev,
+ pmn_pmu->num_counters,
+ sizeof(*pmn_pmu->hw_events.events),
+ GFP_KERNEL);
+ if (!pmn_pmu->hw_events.events) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ raw_spin_lock_init(&pmn_pmu->hw_events.pmu_lock);
+
+ pmn_pmu->hwmod_data = mn_hwmod_data;
+
+ ret = init_hisi_mn_hwcfg(dev, mn_hwmod_data);
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ hisi_free_mn_data(pmn_pmu);
+ return ret;
+}
+
+static struct attribute *hisi_mn_format_attr[] = {
+ HISI_PMU_FORMAT_ATTR(event, "config:0-11"),
+ NULL,
+};
+
+static struct attribute_group hisi_mn_format_group = {
+ .name = "format",
+ .attrs = hisi_mn_format_attr,
+};
+
+static struct attribute *hisi_mn_events_attr[] = {
+ HISI_PMU_EVENT_ATTR_STR(eo_barrier_req,
+ "event=0x0"),
+ HISI_PMU_EVENT_ATTR_STR(ec_barrier_req,
+ "event=0x01"),
+ HISI_PMU_EVENT_ATTR_STR(dvm_op_req,
+ "event=0x02"),
+ HISI_PMU_EVENT_ATTR_STR(dvm_sync_req,
+ "event=0x03"),
+ HISI_PMU_EVENT_ATTR_STR(read_req,
+ "event=0x04"),
+ HISI_PMU_EVENT_ATTR_STR(write_req,
+ "event=0x05"),
+ NULL,
+};
+
+static struct attribute_group hisi_mn_events_group = {
+ .name = "events",
+ .attrs = hisi_mn_events_attr,
+};
+
+static struct attribute *hisi_mn_attrs[] = {
+ NULL,
+};
+
+struct attribute_group hisi_mn_attr_group = {
+ .attrs = hisi_mn_attrs,
+};
+
+static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
+
+static struct attribute *hisi_mn_cpumask_attrs[] = {
+ &dev_attr_cpumask.attr,
+ NULL,
+};
+
+static const struct attribute_group hisi_mn_cpumask_attr_group = {
+ .attrs = hisi_mn_cpumask_attrs,
+};
+
+static const struct attribute_group *hisi_mn_pmu_attr_groups[] = {
+ &hisi_mn_attr_group,
+ &hisi_mn_format_group,
+ &hisi_mn_events_group,
+ &hisi_mn_cpumask_attr_group,
+ NULL,
+};
+
+static struct hisi_uncore_ops hisi_uncore_mn_ops = {
+ .set_evtype = hisi_set_mn_evtype,
+ .set_event_period = hisi_pmu_set_event_period,
+ .get_event_idx = hisi_mn_get_event_idx,
+ .clear_event_idx = hisi_clear_mn_event_idx,
+ .event_update = hisi_mn_event_update,
+ .enable_counter = hisi_enable_mn_counter,
+ .disable_counter = hisi_disable_mn_counter,
+ .write_counter = hisi_write_mn_counter,
+};
+
+static int hisi_mn_pmu_init(struct device *dev,
+ struct hisi_pmu *pmn_pmu)
+{
+ int ret;
+ /* Read common PMU properties */
+ ret = hisi_uncore_common_fwprop_read(dev, pmn_pmu);
+ if (ret)
+ return ret;
+
+ pmn_pmu->name = kasprintf(GFP_KERNEL, "hisi_mn%d",
+ pmn_pmu->scl_id);
+ pmn_pmu->ops = &hisi_uncore_mn_ops;
+
+ pmn_pmu->dev = dev;
+ /* Pick one core to use for cpumask attributes */
+ cpumask_set_cpu(smp_processor_id(), &pmn_pmu->cpu);
+
+ return 0;
+}
+
+static int hisi_pmu_mn_dev_probe(struct hisi_djtag_client *client)
+{
+ struct hisi_pmu *pmn_pmu = NULL;
+ struct device *dev = &client->dev;
+ int ret;
+
+ pmn_pmu = hisi_pmu_alloc(dev);
+ if (IS_ERR(pmn_pmu))
+ return PTR_ERR(pmn_pmu);
+
+ ret = hisi_mn_pmu_init(dev, pmn_pmu);
+ if (ret)
+ return ret;
+
+ ret = init_hisi_mn_data(dev, pmn_pmu, client);
+ if (ret)
+ goto fail_init;
+
+ /* Register with perf PMU */
+ pmn_pmu->pmu = (struct pmu) {
+ .name = pmn_pmu->name,
+ .task_ctx_nr = perf_invalid_context,
+ .event_init = hisi_uncore_pmu_event_init,
+ .add = hisi_uncore_pmu_add,
+ .del = hisi_uncore_pmu_del,
+ .start = hisi_uncore_pmu_start,
+ .stop = hisi_uncore_pmu_stop,
+ .read = hisi_uncore_pmu_read,
+ .attr_groups = hisi_mn_pmu_attr_groups,
+ };
+
+ ret = hisi_uncore_pmu_setup(pmn_pmu, pmn_pmu->name);
+ if (ret) {
+ dev_err(pmn_pmu->dev, "hisi_uncore_pmu_init FAILED!!\n");
+ goto fail;
+ }
+
+ /* Set the drv data to mn_pmu */
+ dev_set_drvdata(dev, pmn_pmu);
+
+ return 0;
+
+fail:
+ hisi_free_mn_data(pmn_pmu);
+
+fail_init:
+ if (pmn_pmu)
+ devm_kfree(dev, pmn_pmu);
+ dev_err(pmn_pmu->dev, "%s failed\n", __func__);
+
+ return ret;
+}
+
+static int hisi_pmu_mn_dev_remove(struct hisi_djtag_client *client)
+{
+ struct hisi_pmu *pmn_pmu = NULL;
+ struct device *dev = &client->dev;
+
+ pmn_pmu = dev_get_drvdata(dev);
+
+ perf_pmu_unregister(&pmn_pmu->pmu);
+ hisi_free_mn_data(pmn_pmu);
+
+ return 0;
+}
+
+static const struct of_device_id mn_of_match[] = {
+ { .compatible = "hisilicon,hisi-pmu-mn-v1", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mn_of_match);
+
+static struct hisi_djtag_driver hisi_pmu_mn_driver = {
+ .driver = {
+ .name = "hisi-pmu-mn",
+ .of_match_table = mn_of_match,
+ },
+ .probe = hisi_pmu_mn_dev_probe,
+ .remove = hisi_pmu_mn_dev_remove,
+};
+
+static int __init hisi_pmu_mn_init(void)
+{
+ int rc;
+
+ rc = hisi_djtag_register_driver(THIS_MODULE, &hisi_pmu_mn_driver);
+ if (rc < 0) {
+ pr_err("hisi pmu mn init failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+module_init(hisi_pmu_mn_init);
+
+static void __exit hisi_pmu_mn_exit(void)
+{
+ hisi_djtag_unregister_driver(&hisi_pmu_mn_driver);
+}
+module_exit(hisi_pmu_mn_exit);
+
+MODULE_DESCRIPTION("HiSilicon SoC HIP0x MN PMU driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Shaokun Zhang");
diff --git a/drivers/perf/hisilicon/hisi_uncore_mn.h b/drivers/perf/hisilicon/hisi_uncore_mn.h
new file mode 100644
index 0000000..4cac2f3
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_mn.h
@@ -0,0 +1,68 @@
+/*
+ * HiSilicon SoC MN Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __HISI_UNCORE_MN_H__
+#define __HISI_UNCORE_MN_H__
+
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon MN RAW event types.
+ */
+enum armv8_hisi_mn_event_types {
+ HISI_HWEVENT_MN_EO_BARR_REQ = 0x0,
+ HISI_HWEVENT_MN_EC_BARR_REQ = 0x01,
+ HISI_HWEVENT_MN_DVM_OP_REQ = 0x02,
+ HISI_HWEVENT_MN_DVM_SYNC_REQ = 0x03,
+ HISI_HWEVENT_MN_READ_REQ = 0x04,
+ HISI_HWEVENT_MN_WRITE_REQ = 0x05,
+ HISI_HWEVENT_MN_COPYBK_REQ = 0x06,
+ HISI_HWEVENT_MN_OTHER_REQ = 0x07,
+ HISI_HWEVENT_MN_EVENT_MAX = 0x08,
+};
+
+/*
+ * ARMv8 HiSilicon Hardware counter Index.
+ */
+enum armv8_hisi_mn_counters {
+ HISI_IDX_MN_COUNTER0 = 0x0,
+ HISI_IDX_MN_COUNTER_MAX = 0x4,
+};
+
+#define HISI_MAX_CFG_MN_CNTR 0x04
+
+struct hisi_mn_hwcfg {
+ u32 evtype_reg0_off;
+ u32 counter_reg0_off;
+ u32 event_ctrl_reg_off;
+ u32 event_enable;
+ u32 module_id;
+ u32 bank_cfgen;
+};
+
+struct hisi_mn_data {
+ struct hisi_djtag_client *client;
+ DECLARE_BITMAP(hisi_mn_event_used_mask,
+ HISI_MAX_CFG_MN_CNTR);
+ struct hisi_mn_hwcfg mn_hwcfg;
+};
+
+#endif /* __HISI_UNCORE_MN_H__ */
--
2.1.4
^ permalink raw reply related
* [PATCH v1 08/11] perf: hisi: Add sysfs attributes for L3 cache(L3C) PMU
From: Anurup M @ 2016-11-02 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478101374-18778-1-git-send-email-anurup.m@huawei.com>
1. Add L3 caches events to /sys/devices/hisi_l3c2/events/
The events can be selected as shown in perf list
e.g.: For L3C_READ_ALLOCATE event for Super CPU cluster 2 the
event format is
-e "hisi_l3c2/read_allocate/"
2. Add cpu_mask attribute group for showing the available CPU
for counting.
Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
---
drivers/perf/hisilicon/hisi_uncore_l3c.c | 57 ++++++++++++++++++++++++++++++++
drivers/perf/hisilicon/hisi_uncore_pmu.c | 40 ++++++++++++++++++++++
drivers/perf/hisilicon/hisi_uncore_pmu.h | 22 ++++++++++++
3 files changed, 119 insertions(+)
diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c.c b/drivers/perf/hisilicon/hisi_uncore_l3c.c
index f78f7b2..428fba0 100644
--- a/drivers/perf/hisilicon/hisi_uncore_l3c.c
+++ b/drivers/perf/hisilicon/hisi_uncore_l3c.c
@@ -436,6 +436,62 @@ static int init_hisi_l3c_data(struct device *dev,
return ret;
}
+static struct attribute *hisi_l3c_format_attr[] = {
+ HISI_PMU_FORMAT_ATTR(event, "config:0-11"),
+ NULL,
+};
+
+static struct attribute_group hisi_l3c_format_group = {
+ .name = "format",
+ .attrs = hisi_l3c_format_attr,
+};
+
+static struct attribute *hisi_l3c_events_attr[] = {
+ HISI_PMU_EVENT_ATTR_STR(read_allocate,
+ "event=0x0"),
+ HISI_PMU_EVENT_ATTR_STR(write_allocate,
+ "event=0x01"),
+ HISI_PMU_EVENT_ATTR_STR(read_noallocate,
+ "event=0x02"),
+ HISI_PMU_EVENT_ATTR_STR(write_noallocate,
+ "event=0x03"),
+ HISI_PMU_EVENT_ATTR_STR(read_hit, "event=0x04"),
+ HISI_PMU_EVENT_ATTR_STR(write_hit, "event=0x05"),
+ NULL,
+};
+
+static struct attribute_group hisi_l3c_events_group = {
+ .name = "events",
+ .attrs = hisi_l3c_events_attr,
+};
+
+static struct attribute *hisi_l3c_attrs[] = {
+ NULL,
+};
+
+struct attribute_group hisi_l3c_attr_group = {
+ .attrs = hisi_l3c_attrs,
+};
+
+static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
+
+static struct attribute *hisi_l3c_cpumask_attrs[] = {
+ &dev_attr_cpumask.attr,
+ NULL,
+};
+
+static const struct attribute_group hisi_l3c_cpumask_attr_group = {
+ .attrs = hisi_l3c_cpumask_attrs,
+};
+
+static const struct attribute_group *hisi_l3c_pmu_attr_groups[] = {
+ &hisi_l3c_attr_group,
+ &hisi_l3c_format_group,
+ &hisi_l3c_events_group,
+ &hisi_l3c_cpumask_attr_group,
+ NULL,
+};
+
static struct hisi_uncore_ops hisi_uncore_l3c_ops = {
.set_evtype = hisi_set_l3c_evtype,
.set_event_period = hisi_pmu_set_event_period,
@@ -496,6 +552,7 @@ static int hisi_pmu_l3c_dev_probe(struct hisi_djtag_client *client)
.start = hisi_uncore_pmu_start,
.stop = hisi_uncore_pmu_stop,
.read = hisi_uncore_pmu_read,
+ .attr_groups = hisi_l3c_pmu_attr_groups,
};
ret = hisi_uncore_pmu_setup(pl3c_pmu, pl3c_pmu->name);
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c
index 8d29fcc..d0a911a 100644
--- a/drivers/perf/hisilicon/hisi_uncore_pmu.c
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c
@@ -26,6 +26,46 @@
#include <linux/perf_event.h>
#include "hisi_uncore_pmu.h"
+/*
+ * PMU format attributes
+ */
+ssize_t hisi_format_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dev_ext_attribute *eattr;
+
+ eattr = container_of(attr, struct dev_ext_attribute,
+ attr);
+ return sprintf(buf, "%s\n", (char *) eattr->var);
+}
+
+/*
+ * PMU event attributes
+ */
+ssize_t hisi_event_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct perf_pmu_events_attr *pmu_attr =
+ container_of(attr, struct perf_pmu_events_attr, attr);
+
+ if (pmu_attr->event_str)
+ return sprintf(buf, "%s", pmu_attr->event_str);
+
+ return 0;
+}
+
+/*
+ * sysfs cpumask attributes
+ */
+ssize_t hisi_cpumask_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pmu *pmu = dev_get_drvdata(dev);
+ struct hisi_pmu *hisi_pmu = to_hisi_pmu(pmu);
+
+ return cpumap_print_to_pagebuf(true, buf, &hisi_pmu->cpu);
+}
+
/* djtag read interface - Call djtag driver to access SoC registers */
int hisi_djtag_readreg(int module_id, int bank, u32 offset,
struct hisi_djtag_client *client, u32 *pvalue)
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h
index b6b16df..a948752 100644
--- a/drivers/perf/hisilicon/hisi_uncore_pmu.h
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h
@@ -51,6 +51,22 @@
(((event_code & HISI_SCCL_MASK) >> \
HISI_SCCL_SHIFT) - 1)
+#define HISI_PMU_FORMAT_ATTR(_name, _config) \
+ (&((struct dev_ext_attribute[]) { \
+ { .attr = __ATTR(_name, 0444, \
+ hisi_format_sysfs_show, NULL), \
+ .var = (void *) _config, \
+ } \
+ })[0].attr.attr)
+
+#define HISI_PMU_EVENT_ATTR_STR(_name, _str) \
+ (&((struct perf_pmu_events_attr[]) { \
+ { .attr = __ATTR(_name, 0444, \
+ hisi_event_sysfs_show, NULL), \
+ .event_str = _str, \
+ } \
+ })[0].attr.attr)
+
struct hisi_pmu;
struct hisi_uncore_ops {
@@ -105,4 +121,10 @@ int hisi_djtag_writereg(int module_id, int bank,
struct hisi_pmu *hisi_pmu_alloc(struct device *dev);
int hisi_uncore_common_fwprop_read(struct device *dev,
struct hisi_pmu *phisi_pmu);
+ssize_t hisi_event_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+ssize_t hisi_format_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+ssize_t hisi_cpumask_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
#endif /* __HISI_UNCORE_PMU_H__ */
--
2.1.4
^ permalink raw reply related
* [PATCH v1 07/11] perf: hisi: Add support for Hisilicon SoC event counters
From: Anurup M @ 2016-11-02 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478101374-18778-1-git-send-email-anurup.m@huawei.com>
1. Hip05/06/07 uncore PMU to support different hardware
event counters.
2. Hisilicon PMU shall use the DJTAG hardware interface
to access hardware event counters and configuration
register.
3. Routines to initialize and setup PMU.
4. Routines to enable/disable/add/del/start/stop hardware
event counting.
5. Add support to count L3 cache hardware events.
Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
drivers/perf/Makefile | 1 +
drivers/perf/hisilicon/Makefile | 1 +
drivers/perf/hisilicon/hisi_uncore_l3c.c | 571 +++++++++++++++++++++++++++++++
drivers/perf/hisilicon/hisi_uncore_l3c.h | 67 ++++
drivers/perf/hisilicon/hisi_uncore_pmu.c | 331 ++++++++++++++++++
drivers/perf/hisilicon/hisi_uncore_pmu.h | 108 ++++++
6 files changed, 1079 insertions(+)
create mode 100644 drivers/perf/hisilicon/Makefile
create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c.c
create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c.h
create mode 100644 drivers/perf/hisilicon/hisi_uncore_pmu.c
create mode 100644 drivers/perf/hisilicon/hisi_uncore_pmu.h
diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index b116e98..061f229 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_ARM_PMU) += arm_pmu.o
obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
+obj-$(CONFIG_HISI_PMU) += hisilicon/
diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
new file mode 100644
index 0000000..e1766cf
--- /dev/null
+++ b/drivers/perf/hisilicon/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c.c b/drivers/perf/hisilicon/hisi_uncore_l3c.c
new file mode 100644
index 0000000..f78f7b2
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_l3c.c
@@ -0,0 +1,571 @@
+/*
+ * HiSilicon SoC L3C Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/bitmap.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_l3c.h"
+
+static inline int hisi_l3c_counter_valid(int idx)
+{
+ return (idx >= HISI_IDX_L3C_COUNTER0 &&
+ idx <= HISI_IDX_L3C_COUNTER_MAX);
+}
+
+static u32 hisi_read_l3c_counter(struct hisi_l3c_data *l3c_hwmod_data,
+ int cntr_idx, int bank_idx)
+{
+ struct hisi_djtag_client *client = l3c_hwmod_data->client;
+ u32 module_id = l3c_hwmod_data->l3c_hwcfg.module_id[bank_idx];
+ u32 cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[bank_idx];
+ u32 reg_offset, value;
+
+ reg_offset = l3c_hwmod_data->l3c_hwcfg.counter_reg0_off +
+ (cntr_idx * 4);
+
+ hisi_djtag_readreg(module_id, cfg_en, reg_offset, client, &value);
+
+ return value;
+}
+
+static u64 hisi_l3c_event_update(struct perf_event *event,
+ struct hw_perf_event *hwc, int idx)
+{
+ struct hisi_pmu *pl3c_pmu = to_hisi_pmu(event->pmu);
+ struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+ u64 delta, prev_raw_count, total_raw_count = 0, avg_raw_count = 0;
+ u32 num_banks = l3c_hwmod_data->l3c_hwcfg.num_banks;
+ int i;
+
+ if (!hisi_l3c_counter_valid(idx)) {
+ dev_err(pl3c_pmu->dev, "Unsupported event index:%d!\n", idx);
+ return 0;
+ }
+
+ /* Check if the L3C data is initialized for this SCCL */
+ if (!l3c_hwmod_data->client) {
+ dev_err(pl3c_pmu->dev, "SCL=%d not initialized!\n",
+ pl3c_pmu->scl_id);
+ return 0;
+ }
+
+ do {
+ /* Get count from individual L3C banks and sum them up */
+ for (i = 0; i < num_banks; i++) {
+ total_raw_count += hisi_read_l3c_counter(l3c_hwmod_data,
+ idx, i);
+ }
+ prev_raw_count = local64_read(&hwc->prev_count);
+
+ /*
+ * As prev_raw_count is updated with average value of
+ * L3 cache banks, we multiply it by no of banks and
+ * compute the delta
+ */
+ delta = (total_raw_count - (prev_raw_count * num_banks)) &
+ HISI_MAX_PERIOD;
+
+ local64_add(delta, &event->count);
+
+ /*
+ * Divide by num of banks to get average count and
+ * update prev_count with this value
+ */
+ avg_raw_count = total_raw_count / num_banks;
+ } while (local64_cmpxchg(
+ &hwc->prev_count, prev_raw_count, avg_raw_count) !=
+ prev_raw_count);
+
+ return total_raw_count;
+}
+
+static void hisi_set_l3c_evtype(struct hisi_pmu *pl3c_pmu, int idx, u32 val)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+ u32 reg_offset = l3c_hwmod_data->l3c_hwcfg.evtype_reg0_off;
+ u32 event_value, value = 0;
+ u32 cfg_en, module_id;
+ int i;
+
+ event_value = (val -
+ HISI_HWEVENT_L3C_READ_ALLOCATE);
+
+ /* Select the appropriate Event select register */
+ if (idx > 3)
+ reg_offset += 4;
+
+ /* Value to write to event type register */
+ val = event_value << (8 * idx);
+
+ /* Find the djtag Identifier of the Unit */
+ client = l3c_hwmod_data->client;
+
+ /*
+ * Set the event in L3C_EVENT_TYPEx Register
+ * for all L3C banks
+ */
+ for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+ module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+ cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client, &value);
+
+ value &= ~(0xff << (8 * idx));
+ value |= val;
+
+ hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+ }
+}
+
+static u32 hisi_write_l3c_counter(struct hisi_pmu *pl3c_pmu,
+ struct hw_perf_event *hwc, u32 value)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+ u32 reg_offset, cfg_en, module_id;
+ int i, ret = 0;
+ int idx = GET_CNTR_IDX(hwc);
+
+ if (!hisi_l3c_counter_valid(idx)) {
+ dev_err(pl3c_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return -EINVAL;
+ }
+
+ reg_offset = l3c_hwmod_data->l3c_hwcfg.counter_reg0_off +
+ (idx * 4);
+
+ client = l3c_hwmod_data->client;
+
+ for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+ module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+ cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+ ret = hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+ if (!ret)
+ ret = value;
+ }
+
+ return ret;
+}
+
+static void hisi_enable_l3c_counter(struct hisi_pmu *pl3c_pmu, int idx)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+ u32 reg_offset = l3c_hwmod_data->l3c_hwcfg.event_ctrl_reg_off;
+ u32 eventen = l3c_hwmod_data->l3c_hwcfg.event_enable;
+ u32 value, cfg_en, module_id;
+ int i;
+
+ if (!hisi_l3c_counter_valid(idx)) {
+ dev_err(pl3c_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return;
+ }
+
+ client = l3c_hwmod_data->client;
+
+ /*
+ * Set the event_bus_en bit in L3C AUCNTRL to enable counting
+ * for all L3C banks
+ */
+ for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+ module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+ cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client, &value);
+
+ value |= eventen;
+ hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+ }
+}
+
+static void hisi_disable_l3c_counter(struct hisi_pmu *pl3c_pmu, int idx)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+ u32 reg_offset = l3c_hwmod_data->l3c_hwcfg.event_ctrl_reg_off;
+ u32 eventen = l3c_hwmod_data->l3c_hwcfg.event_enable;
+ u32 value, cfg_en, module_id;
+ int i;
+
+ if (!hisi_l3c_counter_valid(idx)) {
+ dev_err(pl3c_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return;
+ }
+
+ /* Find the djtag Identifier of the Unit */
+ client = l3c_hwmod_data->client;
+
+ /*
+ * Clear the event_bus_en bit in L3C AUCNTRL if no other
+ * event counting for all L3C banks
+ */
+ for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+ module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+ cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client, &value);
+
+ value &= ~(eventen);
+ hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+ }
+}
+
+static void hisi_clear_l3c_event_idx(struct hisi_pmu *pl3c_pmu,
+ int idx)
+{
+ struct hisi_djtag_client *client;
+ struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+ u32 reg_offset = l3c_hwmod_data->l3c_hwcfg.evtype_reg0_off;
+ void *bitmap_addr;
+ u32 cfg_en, value, module_id;
+ int i;
+
+ if (!hisi_l3c_counter_valid(idx)) {
+ dev_err(pl3c_pmu->dev,
+ "Unsupported event index:%d!\n", idx);
+ return;
+ }
+
+ bitmap_addr = l3c_hwmod_data->hisi_l3c_event_used_mask;
+
+ __clear_bit(idx, bitmap_addr);
+
+ /* Clear Counting in L3C event config register */
+ if (idx > 3)
+ reg_offset += 4;
+
+ client = l3c_hwmod_data->client;
+
+ /*
+ * Clear the event in L3C_EVENT_TYPEx Register
+ * for all L3C banks
+ */
+ for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+ module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+ cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+ hisi_djtag_readreg(module_id,
+ cfg_en,
+ reg_offset,
+ client, &value);
+
+ value &= ~(0xff << (8 * idx));
+ value |= (0xff << (8 * idx));
+ hisi_djtag_writereg(module_id,
+ cfg_en,
+ reg_offset,
+ value,
+ client);
+ }
+}
+
+static int hisi_l3c_get_event_idx(struct hisi_pmu *pl3c_pmu)
+{
+ struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+ int event_idx;
+
+ event_idx =
+ find_first_zero_bit(
+ l3c_hwmod_data->hisi_l3c_event_used_mask,
+ pl3c_pmu->num_counters);
+
+ if (event_idx == HISI_MAX_CFG_L3C_CNTR)
+ return -EAGAIN;
+
+ __set_bit(event_idx,
+ l3c_hwmod_data->hisi_l3c_event_used_mask);
+
+ return event_idx;
+}
+
+static void hisi_free_l3c_data(struct hisi_pmu *pl3c_pmu)
+{
+ kfree(pl3c_pmu->hwmod_data);
+ pl3c_pmu->hwmod_data = NULL;
+}
+
+static int init_hisi_l3c_hwcfg(struct device *dev,
+ struct hisi_l3c_data *pl3c_data)
+{
+ struct hisi_l3c_hwcfg *pl3c_hwcfg = &pl3c_data->l3c_hwcfg;
+ struct device_node *node = dev->of_node;
+ u32 prop_len;
+ int ret;
+
+ if (of_property_read_u32(node, "counter-reg",
+ &pl3c_hwcfg->counter_reg0_off)) {
+ dev_err(dev, "DT:Couldnot read counter-reg!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "evctrl-reg",
+ &pl3c_hwcfg->event_ctrl_reg_off)) {
+ dev_err(dev, "DT:Couldnot read evctrl-reg!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "event-en",
+ &pl3c_hwcfg->event_enable)) {
+ dev_err(dev, "DT:Couldnot read event-en!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "evtype-reg",
+ &pl3c_hwcfg->evtype_reg0_off)) {
+ dev_err(dev, "DT:Couldnot read evtype-reg!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "num-banks",
+ &pl3c_hwcfg->num_banks)) {
+ dev_err(dev, "DT:Couldnot read num-banks!\n");
+ return -EINVAL;
+ }
+
+ prop_len = of_property_count_u32_elems(node, "module-id");
+ if (prop_len != pl3c_hwcfg->num_banks) {
+ dev_err(dev, "DT:module-id entry not valid!\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "module-id",
+ &pl3c_hwcfg->module_id[0],
+ pl3c_hwcfg->num_banks);
+ if (ret < 0) {
+ dev_err(dev, "DT:Couldnot read module-id!\n");
+ return -EINVAL;
+ }
+
+ prop_len = of_property_count_u32_elems(node, "cfgen-map");
+ if (prop_len != pl3c_hwcfg->num_banks) {
+ dev_err(dev, "DT:cfgen-map entrynot valid!\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "cfgen-map",
+ &pl3c_hwcfg->bank_cfgen[0],
+ pl3c_hwcfg->num_banks);
+ if (ret < 0) {
+ dev_err(dev, "DT:Couldnot read cfgen-map!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int init_hisi_l3c_data(struct device *dev,
+ struct hisi_pmu *pl3c_pmu,
+ struct hisi_djtag_client *client)
+{
+ struct hisi_l3c_data *l3c_hwmod_data = NULL;
+ int ret;
+
+ l3c_hwmod_data = kzalloc(sizeof(struct hisi_l3c_data),
+ GFP_KERNEL);
+ if (!l3c_hwmod_data)
+ return -ENOMEM;
+
+ /* Set the djtag Identifier */
+ l3c_hwmod_data->client = client;
+
+ pl3c_pmu->hw_events.events = devm_kcalloc(dev,
+ pl3c_pmu->num_counters,
+ sizeof(*pl3c_pmu->hw_events.events),
+ GFP_KERNEL);
+ if (!pl3c_pmu->hw_events.events) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ raw_spin_lock_init(&pl3c_pmu->hw_events.pmu_lock);
+
+ pl3c_pmu->hwmod_data = l3c_hwmod_data;
+
+ ret = init_hisi_l3c_hwcfg(dev, l3c_hwmod_data);
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ hisi_free_l3c_data(pl3c_pmu);
+ return ret;
+}
+
+static struct hisi_uncore_ops hisi_uncore_l3c_ops = {
+ .set_evtype = hisi_set_l3c_evtype,
+ .set_event_period = hisi_pmu_set_event_period,
+ .get_event_idx = hisi_l3c_get_event_idx,
+ .clear_event_idx = hisi_clear_l3c_event_idx,
+ .event_update = hisi_l3c_event_update,
+ .enable_counter = hisi_enable_l3c_counter,
+ .disable_counter = hisi_disable_l3c_counter,
+ .write_counter = hisi_write_l3c_counter,
+};
+
+static int hisi_l3c_pmu_init(struct device *dev,
+ struct hisi_pmu *pl3c_pmu)
+{
+ int ret;
+
+ /* Read common PMU properties */
+ ret = hisi_uncore_common_fwprop_read(dev, pl3c_pmu);
+ if (ret)
+ return ret;
+
+ pl3c_pmu->name = kasprintf(GFP_KERNEL, "hisi_l3c%d",
+ pl3c_pmu->scl_id);
+ pl3c_pmu->ops = &hisi_uncore_l3c_ops;
+ pl3c_pmu->dev = dev;
+
+ /* Pick one core to use for cpumask attributes */
+ cpumask_set_cpu(smp_processor_id(), &pl3c_pmu->cpu);
+
+ return 0;
+}
+
+static int hisi_pmu_l3c_dev_probe(struct hisi_djtag_client *client)
+{
+ struct hisi_pmu *pl3c_pmu = NULL;
+ struct device *dev = &client->dev;
+ int ret;
+
+ pl3c_pmu = hisi_pmu_alloc(dev);
+ if (IS_ERR(pl3c_pmu))
+ return PTR_ERR(pl3c_pmu);
+
+ ret = hisi_l3c_pmu_init(dev, pl3c_pmu);
+ if (ret)
+ return ret;
+
+ ret = init_hisi_l3c_data(dev, pl3c_pmu, client);
+ if (ret)
+ goto fail_init;
+
+ /* Register with perf PMU */
+ pl3c_pmu->pmu = (struct pmu) {
+ .name = pl3c_pmu->name,
+ .task_ctx_nr = perf_invalid_context,
+ .event_init = hisi_uncore_pmu_event_init,
+ .add = hisi_uncore_pmu_add,
+ .del = hisi_uncore_pmu_del,
+ .start = hisi_uncore_pmu_start,
+ .stop = hisi_uncore_pmu_stop,
+ .read = hisi_uncore_pmu_read,
+ };
+
+ ret = hisi_uncore_pmu_setup(pl3c_pmu, pl3c_pmu->name);
+ if (ret) {
+ dev_err(dev, "hisi_uncore_pmu_init FAILED!!\n");
+ goto fail;
+ }
+
+ /* Set the drv data to l3c_pmu */
+ dev_set_drvdata(dev, pl3c_pmu);
+
+ return 0;
+
+fail:
+ hisi_free_l3c_data(pl3c_pmu);
+
+fail_init:
+ dev_err(dev, "%s failed\n", __func__);
+ return ret;
+}
+
+static int hisi_pmu_l3c_dev_remove(struct hisi_djtag_client *client)
+{
+ struct hisi_pmu *pl3c_pmu = NULL;
+ struct device *dev = &client->dev;
+
+ pl3c_pmu = dev_get_drvdata(dev);
+
+ perf_pmu_unregister(&pl3c_pmu->pmu);
+ hisi_free_l3c_data(pl3c_pmu);
+
+ return 0;
+}
+
+static const struct of_device_id l3c_of_match[] = {
+ { .compatible = "hisilicon,hisi-pmu-l3c-v1", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, l3c_of_match);
+
+static struct hisi_djtag_driver hisi_pmu_l3c_driver = {
+ .driver = {
+ .name = "hisi-pmu-l3c",
+ .of_match_table = l3c_of_match,
+ },
+ .probe = hisi_pmu_l3c_dev_probe,
+ .remove = hisi_pmu_l3c_dev_remove,
+};
+
+static int __init hisi_pmu_l3c_init(void)
+{
+ int rc;
+
+ rc = hisi_djtag_register_driver(THIS_MODULE, &hisi_pmu_l3c_driver);
+ if (rc < 0) {
+ pr_err("hisi pmu l3c init failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+module_init(hisi_pmu_l3c_init);
+
+static void __exit hisi_pmu_l3c_exit(void)
+{
+ hisi_djtag_unregister_driver(&hisi_pmu_l3c_driver);
+
+}
+module_exit(hisi_pmu_l3c_exit);
+
+MODULE_DESCRIPTION("HiSilicon SoC HIP0x L3C PMU driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Anurup M");
diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c.h b/drivers/perf/hisilicon/hisi_uncore_l3c.h
new file mode 100644
index 0000000..a4a1777
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_l3c.h
@@ -0,0 +1,67 @@
+/*
+ * HiSilicon SoC L3C Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __HISI_UNCORE_L3C_H__
+#define __HISI_UNCORE_L3C_H__
+
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon L3C RAW event types.
+ */
+enum armv8_hisi_l3c_event_types {
+ HISI_HWEVENT_L3C_READ_ALLOCATE = 0x0,
+ HISI_HWEVENT_L3C_WRITE_ALLOCATE = 0x01,
+ HISI_HWEVENT_L3C_READ_NOALLOCATE = 0x02,
+ HISI_HWEVENT_L3C_WRITE_NOALLOCATE = 0x03,
+ HISI_HWEVENT_L3C_READ_HIT = 0x04,
+ HISI_HWEVENT_L3C_WRITE_HIT = 0x05,
+ HISI_HWEVENT_L3C_EVENT_MAX = 0x15,
+};
+
+/*
+ * ARMv8 HiSilicon Hardware counter Index.
+ */
+enum armv8_hisi_l3c_counters {
+ HISI_IDX_L3C_COUNTER0 = 0x0,
+ HISI_IDX_L3C_COUNTER_MAX = 0x7,
+};
+
+#define HISI_MAX_CFG_L3C_CNTR 0x08
+
+struct hisi_l3c_hwcfg {
+ u32 evtype_reg0_off;
+ u32 counter_reg0_off;
+ u32 event_ctrl_reg_off;
+ u32 event_enable;
+ u32 module_id[MAX_BANKS];
+ u32 num_banks;
+ u32 bank_cfgen[MAX_BANKS];
+};
+
+struct hisi_l3c_data {
+ struct hisi_djtag_client *client;
+ DECLARE_BITMAP(hisi_l3c_event_used_mask,
+ HISI_MAX_CFG_L3C_CNTR);
+ struct hisi_l3c_hwcfg l3c_hwcfg;
+};
+
+#endif /* __HISI_UNCORE_L3C_H__ */
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c
new file mode 100644
index 0000000..8d29fcc
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c
@@ -0,0 +1,331 @@
+/*
+ * HiSilicon SoC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/bitmap.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_pmu.h"
+
+/* djtag read interface - Call djtag driver to access SoC registers */
+int hisi_djtag_readreg(int module_id, int bank, u32 offset,
+ struct hisi_djtag_client *client, u32 *pvalue)
+{
+ int ret;
+ u32 chain_id = 0;
+
+ while (bank != 1) {
+ bank = (bank >> 0x1);
+ chain_id++;
+ }
+
+ ret = hisi_djtag_readl(client, offset, module_id,
+ chain_id, pvalue);
+ if (ret)
+ dev_err(&client->dev, "read failed, ret=%d!\n", ret);
+
+ return ret;
+}
+
+/* djtag write interface - Call djtag driver to access SoC registers */
+int hisi_djtag_writereg(int module_id, int bank,
+ u32 offset, u32 value,
+ struct hisi_djtag_client *client)
+{
+ int ret;
+
+ ret = hisi_djtag_writel(client, offset, module_id,
+ HISI_DJTAG_MOD_MASK, value);
+ if (ret)
+ dev_err(&client->dev, "write failed, ret=%d!\n", ret);
+
+ return ret;
+}
+
+static int pmu_map_event(struct perf_event *event)
+{
+ return (int)(event->attr.config & HISI_EVTYPE_EVENT);
+}
+
+static int
+__hw_perf_event_init(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+ struct device *dev = phisi_pmu->dev;
+ int mapping;
+
+ mapping = pmu_map_event(event);
+ if (mapping < 0) {
+ dev_err(dev, "event %x:%llx not supported\n", event->attr.type,
+ event->attr.config);
+ return mapping;
+ }
+
+ /*
+ * We don't assign an index until we actually place the event onto
+ * hardware. Use -1 to signify that we haven't decided where to put it
+ * yet.
+ */
+ hwc->idx = -1;
+ hwc->config = 0;
+ hwc->event_base = 0;
+
+ /* For HiSilicon SoC L3C update config_base based on event encoding */
+ hwc->config_base = event->attr.config;
+
+ return 0;
+}
+
+int hisi_uncore_pmu_event_init(struct perf_event *event)
+{
+ int err;
+ struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+ if (event->attr.type != event->pmu->type)
+ return -ENOENT;
+
+ /* we do not support sampling as the counters are all
+ * shared by all CPU cores in a CPU die(SCCL). Also we
+ * donot support attach to a task(per-process mode)
+ */
+ if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
+ return -EOPNOTSUPP;
+
+ /* counters do not have these bits */
+ if (event->attr.exclude_user ||
+ event->attr.exclude_kernel ||
+ event->attr.exclude_host ||
+ event->attr.exclude_guest ||
+ event->attr.exclude_hv ||
+ event->attr.exclude_idle)
+ return -EINVAL;
+
+ if (event->cpu < 0)
+ return -EINVAL;
+
+ event->cpu = cpumask_first(&phisi_pmu->cpu);
+
+ err = __hw_perf_event_init(event);
+
+ return err;
+}
+
+/*
+ * Enable counter and set the counter to count
+ * the event that we're interested in.
+ */
+void hisi_uncore_pmu_enable_event(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+ /* Disable the hardware event counting */
+ if (phisi_pmu->ops->disable_counter)
+ phisi_pmu->ops->disable_counter(phisi_pmu, GET_CNTR_IDX(hwc));
+
+ /*
+ * Set event (if destined for Hisilicon SoC counters).
+ */
+ if (phisi_pmu->ops->set_evtype)
+ phisi_pmu->ops->set_evtype(phisi_pmu, GET_CNTR_IDX(hwc),
+ hwc->config_base);
+
+ /* Enable the hardware event counting */
+ if (phisi_pmu->ops->enable_counter)
+ phisi_pmu->ops->enable_counter(phisi_pmu, GET_CNTR_IDX(hwc));
+}
+
+void hisi_pmu_set_event_period(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+ /*
+ * The Hisilicon PMU counters have a period of 2^32. To account for the
+ * possiblity of extreme interrupt latency we program for a period of
+ * half that. Hopefully we can handle the interrupt before another 2^31
+ * events occur and the counter overtakes its previous value.
+ */
+ u64 val = 1ULL << 31;
+
+ local64_set(&hwc->prev_count, val);
+
+ /* Write to the hardware event counter */
+ phisi_pmu->ops->write_counter(phisi_pmu, hwc, val);
+}
+
+void hisi_uncore_pmu_start(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+ struct hisi_pmu_hw_events *hw_events;
+
+ hw_events = &phisi_pmu->hw_events;
+
+ if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
+ return;
+
+ WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
+ hwc->state = 0;
+
+ if (phisi_pmu->ops->set_event_period)
+ phisi_pmu->ops->set_event_period(event);
+
+ if (flags & PERF_EF_RELOAD) {
+ u64 prev_raw_count = local64_read(&hwc->prev_count);
+
+ phisi_pmu->ops->write_counter(phisi_pmu, hwc,
+ (u32)prev_raw_count);
+ }
+
+ hisi_uncore_pmu_enable_event(event);
+ perf_event_update_userpage(event);
+}
+
+void hisi_uncore_pmu_stop(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+ if (hwc->state & PERF_HES_UPTODATE)
+ return;
+
+ /*
+ * We always reprogram the counter, so ignore PERF_EF_UPDATE.
+ * See hisi_uncore_pmu_start()
+ */
+ if (phisi_pmu->ops->disable_counter)
+ phisi_pmu->ops->disable_counter(phisi_pmu,
+ GET_CNTR_IDX(hwc));
+
+ WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
+ hwc->state |= PERF_HES_STOPPED;
+ if (hwc->state & PERF_HES_UPTODATE)
+ return;
+
+ /* Read hardware counter and update the Perf counter statistics */
+ phisi_pmu->ops->event_update(event, hwc, GET_CNTR_IDX(hwc));
+ hwc->state |= PERF_HES_UPTODATE;
+}
+
+int hisi_uncore_pmu_add(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+ struct hisi_pmu_hw_events *hw_events;
+ int idx;
+
+ hw_events = &phisi_pmu->hw_events;
+
+ hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+
+ /* If we don't have a free counter then return early. */
+ idx = phisi_pmu->ops->get_event_idx(phisi_pmu);
+ if (idx < 0)
+ return -EAGAIN;
+
+ event->hw.idx = idx;
+ hw_events->events[idx] = event;
+
+ if (flags & PERF_EF_START)
+ hisi_uncore_pmu_start(event, PERF_EF_RELOAD);
+
+ /* Propagate our changes to the userspace mapping. */
+ perf_event_update_userpage(event);
+
+ return 0;
+}
+
+void hisi_uncore_pmu_del(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+ struct hisi_pmu_hw_events *hw_events;
+
+ hw_events = &phisi_pmu->hw_events;
+
+ hisi_uncore_pmu_stop(event, PERF_EF_UPDATE);
+
+ phisi_pmu->ops->clear_event_idx(phisi_pmu, GET_CNTR_IDX(hwc));
+ perf_event_update_userpage(event);
+ hw_events->events[GET_CNTR_IDX(hwc)] = NULL;
+}
+
+struct hisi_pmu *hisi_pmu_alloc(struct device *dev)
+{
+ struct hisi_pmu *phisi_pmu;
+
+ phisi_pmu = devm_kzalloc(dev, sizeof(*phisi_pmu), GFP_KERNEL);
+ if (!phisi_pmu)
+ return ERR_PTR(-ENOMEM);
+
+ return phisi_pmu;
+}
+
+void hisi_uncore_pmu_read(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+ /* Read hardware counter and update the Perf counter statistics */
+ phisi_pmu->ops->event_update(event, hwc, GET_CNTR_IDX(hwc));
+}
+
+int hisi_uncore_common_fwprop_read(struct device *dev,
+ struct hisi_pmu *phisi_pmu)
+{
+ if (device_property_read_u32(dev, "num-events",
+ &phisi_pmu->num_events)) {
+ dev_err(dev, "Cant read num-events from DT!\n");
+ return -EINVAL;
+ }
+
+ if (device_property_read_u32(dev, "num-counters",
+ &phisi_pmu->num_counters)) {
+ dev_err(dev, "Cant read num-counters from DT!\n");
+ return -EINVAL;
+ }
+
+ /* Find the SCL ID */
+ if (device_property_read_u32(dev, "scl-id",
+ &phisi_pmu->scl_id)) {
+ dev_err(dev, "Cant read scl-id!\n");
+ return -EINVAL;
+ }
+
+ if (phisi_pmu->scl_id == 0 ||
+ phisi_pmu->scl_id >= MAX_UNITS) {
+ dev_err(dev, "Invalid SCL=%d!\n",
+ phisi_pmu->scl_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int hisi_uncore_pmu_setup(struct hisi_pmu *phisi_pmu,
+ const char *pmu_name)
+{
+ /* Register the events with perf */
+ return perf_pmu_register(&phisi_pmu->pmu, pmu_name, -1);
+}
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h
new file mode 100644
index 0000000..b6b16df
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h
@@ -0,0 +1,108 @@
+/*
+ * HiSilicon SoC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __HISI_UNCORE_PMU_H__
+#define __HISI_UNCORE_PMU_H__
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/soc/hisilicon/djtag.h>
+#include <linux/types.h>
+#include <asm/local64.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt) "hisi_pmu: " fmt
+
+#define HISI_DJTAG_MOD_MASK (0xFFFF)
+#define HISI_CNTR_SCCL_MASK (0xF00)
+
+#define HISI_EVTYPE_EVENT 0xfff
+#define HISI_MAX_PERIOD ((1LLU << 32) - 1)
+
+#define MAX_BANKS 8
+#define MAX_COUNTERS 30
+#define MAX_UNITS 8
+
+#define GET_CNTR_IDX(hwc) (hwc->idx)
+#define to_hisi_pmu(c) (container_of(c, struct hisi_pmu, pmu))
+
+#define GET_UNIT_IDX(event_code) \
+ (((event_code & HISI_SCCL_MASK) >> \
+ HISI_SCCL_SHIFT) - 1)
+
+struct hisi_pmu;
+
+struct hisi_uncore_ops {
+ void (*set_evtype)(struct hisi_pmu *, int, u32);
+ void (*set_event_period)(struct perf_event *);
+ int (*get_event_idx)(struct hisi_pmu *);
+ void (*clear_event_idx)(struct hisi_pmu *, int);
+ u64 (*event_update)(struct perf_event *,
+ struct hw_perf_event *, int);
+ u32 (*read_counter)(struct hisi_pmu *, int, int);
+ u32 (*write_counter)(struct hisi_pmu *,
+ struct hw_perf_event *, u32);
+ void (*enable_counter)(struct hisi_pmu *, int);
+ void (*disable_counter)(struct hisi_pmu *, int);
+};
+
+struct hisi_pmu_hw_events {
+ struct perf_event **events;
+ raw_spinlock_t pmu_lock;
+};
+
+/* Generic pmu struct for different pmu types */
+struct hisi_pmu {
+ const char *name;
+ struct hisi_pmu_hw_events hw_events;
+ struct hisi_uncore_ops *ops;
+ struct device *dev;
+ void *hwmod_data; /* Hardware module specific data */
+ cpumask_t cpu;
+ struct pmu pmu;
+ u32 scl_id;
+ int num_counters;
+ int num_events;
+ int num_units;
+};
+
+void hisi_uncore_pmu_read(struct perf_event *event);
+void hisi_uncore_pmu_del(struct perf_event *event, int flags);
+int hisi_uncore_pmu_add(struct perf_event *event, int flags);
+void hisi_uncore_pmu_start(struct perf_event *event, int flags);
+void hisi_uncore_pmu_stop(struct perf_event *event, int flags);
+void hisi_pmu_set_event_period(struct perf_event *event);
+void hisi_uncore_pmu_enable_event(struct perf_event *event);
+int hisi_uncore_pmu_setup(struct hisi_pmu *phisi_pmu, const char *pmu_name);
+int hisi_uncore_pmu_event_init(struct perf_event *event);
+int hisi_djtag_readreg(int module_id, int bank, u32 offset,
+ struct hisi_djtag_client *client,
+ u32 *pvalue);
+int hisi_djtag_writereg(int module_id, int bank,
+ u32 offset, u32 value,
+ struct hisi_djtag_client *client);
+struct hisi_pmu *hisi_pmu_alloc(struct device *dev);
+int hisi_uncore_common_fwprop_read(struct device *dev,
+ struct hisi_pmu *phisi_pmu);
+#endif /* __HISI_UNCORE_PMU_H__ */
--
2.1.4
^ permalink raw reply related
* [PATCH v1 06/11] perf: hisi: Update Kconfig for Hisilicon PMU support
From: Anurup M @ 2016-11-02 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478101374-18778-1-git-send-email-anurup.m@huawei.com>
1. Update Kconfig for Hip05/06/07 PMU support.
Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
drivers/perf/Kconfig | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index 4d5c5f9..da8dd97 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -19,4 +19,13 @@ config XGENE_PMU
help
Say y if you want to use APM X-Gene SoC performance monitors.
+config HISI_PMU
+ bool "Enable hardware event counter support for HiSilicon SoC"
+ depends on HW_PERF_EVENTS && ARM64
+ depends on HISI_DJTAG
+ help
+ Enable hardware event counter support for hardware event counters
+ in Hisilicon hip05/06/07 SoC. The hardware modules like L3C, MN and
+ DDRC have hardware events and counters.
+
endmenu
--
2.1.4
^ permalink raw reply related
* [PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
From: Anurup M @ 2016-11-02 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478101374-18778-1-git-send-email-anurup.m@huawei.com>
1) Device tree bindings for Hisilicon SoC PMU.
2) Add example for Hisilicon L3 cache, MN and DDRC PMU.
Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
---
.../devicetree/bindings/arm/hisilicon/pmu.txt | 127 +++++++++++++++++++++
1 file changed, 127 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
new file mode 100644
index 0000000..e7b35e0
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
@@ -0,0 +1,127 @@
+Hisilicon SoC hip05/06/07 ARMv8 PMU
+===================================
+
+The Hisilicon SoC chips like hip05/06/07 etc. consist of varous independent
+system device PMU's such as L3 cache (L3C), Miscellaneous Nodes(MN) and DDR
+comtroller. These PMU devices are independent and have hardware logic to
+gather statistics and performance information.
+
+HiSilicon SoC chip is encapsulated by multiple CPU and IO die's. The CPU die
+is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
+is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
+e.g. In the case of hip05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
+
+The Hisilicon SoC PMU DT node bindigs for uncore PMU devices are as below.
+For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
+the parent node will be the djtag node of the corresponding CPU die(SCCL).
+
+For uncore PMU devices there are some common required properties as detailed
+below.
+
+Required properties:
+ - compatible : This field contain two values. The first value is
+ always "hisilicon" and second value is the Module type as shown
+ in below examples:
+ (a) "hisilicon,hisi-pmu-l3c-v1" for Hisilicon SoC L3C PMU
+ device (Version 1)
+ (b) "hisilicon,hisi-pmu-mn-v1" for Hisilicon SoC MN PMU
+ device (Version 1)
+ (c) "hisilicon,hisi-pmu-ddrc-v1" for Hisilicon SoC DDRC PMU
+ device (Version 1)
+ The hip05/06/07 chips have v1 hardware for L3C, MN and DDRC.
+
+ - scl-id : The Super Cluster ID. This can be the ID of the CPU die
+ or IO die in the chip.
+
+ - num-events : No of events supported by this PMU device.
+
+ - num-counters : No of hardware counters available for counting.
+
+L3 cache
+--------
+The L3 cache is dedicated for each SCCL and hence there are separate DT nodes
+for L3 cache for each SCCL. For L3 cache PMU the additional required properties
+are
+ - counter-reg : Counter register offset.
+
+ - evtype-reg : Event select register offset.
+
+ - evctrl-reg : Event counting control(LAUCTRL) register offset.
+
+ - event-en : Event enable value.
+
+ - module-id : Module ID to input for djtag. This property is an array of
+ module_id for each L3 cache banks.
+
+ - num-banks : Number of banks or instances of the device.
+
+ - cfgen-map : Config enable array to select the bank.
+
+Miscellaneous Node
+-------------------
+The MN is dedicated for each SCCL and hence there are separate DT nodes for MN
+for each SCCL. For MN PMU the additional required properties are
+ - counter-reg : Counter register offset.
+
+ - evtype-reg : Event select register offset.
+
+ - evctrl-reg : Event counting control register offset.
+
+ - module-id : Module ID to input for djtag. As MN doesnot have multiple banks
+ this property is a single value.
+
+ - cfgen-map : Config enable to select the bank. For MN it is a single value
+
+ - event-en : Event enable value.
+
+Example:
+
+ djtag0: djtag at 0 {
+ compatible = "hisilicon,hip05-cpu-djtag-v1";
+ pmul3c0 {
+ compatible = "hisilicon,hisi-pmu-l3c-v1";
+ scl-id = <0x02>;
+ num-events = <0x16>;
+ num-counters = <0x08>;
+ module-id = <0x04 0x04 0x04 0x04>;
+ num-banks = <0x04>;
+ cfgen-map = <0x02 0x04 0x01 0x08>;
+ counter-reg = <0x170>;
+ evctrl-reg = <0x04>;
+ event-en = <0x1000000>;
+ evtype-reg = <0x140>;
+ };
+
+ pmumn0 {
+ compatible = "hisilicon,hisi-pmu-mn-v1";
+ scl-id = <0x02>;
+ num-events = <0x09>;
+ num-counters = <0x04>;
+ module-id = <0x0b>;
+ cfgen-map = <0x01>;
+ counter-reg = <0x30>;
+ evctrl-reg = <0x40>;
+ event-en = <0x01>;
+ evtype-reg = <0x48>;
+ };
+ };
+
+DDR controller
+--------------
+Each SCCL in Hip05/06/07 chips have 2 DDR channels and hence 2 DDR controllers.
+There are separate DT nodes for each DDR channel.
+For DDRC PMU the additional required properties are
+
+ - ch-id : DDRC Channel ID.
+ - reg : Register base address and range for the DDRC channel.
+
+Example:
+ /* DDRC for CPU die scl #2 Channel #1 for hip05 */
+ pmu_sccl0_ddrc1: pmu_ddrc1 at 80358000 {
+ compatible = "hisilicon,hisi-pmu-ddrc-v1";
+ scl-id = <0x02>;
+ ch-id = <0x1>;
+ num-events = <0x0D>;
+ num-counters = <0x04>;
+ reg = <0x80358000 0x10000>; /* TOTEMC DDRC1 */
+ };
--
2.1.4
^ permalink raw reply related
* [PATCH v1 04/11] Documentation: perf: hisi: Documentation for HIP05/06/07 PMU event counting.
From: Anurup M @ 2016-11-02 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478101374-18778-1-git-send-email-anurup.m@huawei.com>
Documentation for perf usage and Hisilicon SoC PMU uncore events.
The Hisilicon SOC has event counters for hardware modules like
L3 cache, Miscellaneous node, DDR cntroller etc. These events are
all uncore.
Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
---
Documentation/perf/hisi-pmu.txt | 80 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 80 insertions(+)
create mode 100644 Documentation/perf/hisi-pmu.txt
diff --git a/Documentation/perf/hisi-pmu.txt b/Documentation/perf/hisi-pmu.txt
new file mode 100644
index 0000000..670a9df
--- /dev/null
+++ b/Documentation/perf/hisi-pmu.txt
@@ -0,0 +1,80 @@
+Hisilicon SoC PMU (Performance Monitoring Unit)
+================================================
+The Hisilicon SoC hip05/06/07 chips consist of varous independent system
+device PMU's such as L3 cache(L3C), Miscellaneous Nodes(MN) and DDR
+controllers. These PMU devices are independent and have hardware logic to
+gather statistics and performance information.
+
+Hip0x chips are encapsulated by multiple CPU and IO die's. The CPU die is
+called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
+is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
+Each SCCL has 1 L3 cache and 1 MN units.
+
+The L3 cache is shared by all CPU cores in a CPU die. The L3C has four banks
+(or instances). Each bank or instance of L3C has Eight 32-bit counter
+registers. The hip05/06 chip L3 cache has 22 statistics events. The hip07
+chip has 66 statistics events. These events are very useful for debugging.
+
+The MN module is also shared by all CPU cores in a CPU die. It receives
+barriers and DVM(Distributed Virtual Memory) messages from cpu or smmu, and
+perform the required actions and return response messages. These events are
+very useful for debugging. The MN has total 9 statistics events and support
+four 32-bit counter registers in hip05/06/07 chips.
+
+The DDR conroller supports various statistics events. Every SCCL has fot 2
+DDR channels and hence 2 DDR controllers. The Hip05/06/07 has support for a
+total of 13 statistics events.
+
+There is no memory mapping for L3 cache and MN registers. It can be accessed
+by using the Hisilicon djtag interface. The Djtag in a SCCL is an independent
+module which connects with some modules in the SoC by Debug Bus.
+
+Hisilicon SoC (hip05/06/07) PMU driver
+--------------------------------------
+The hip0x PMU driver shall register perf PMU drivers like L3 cache, MN, DDRC
+etc.
+Separate PMU shall be registered for L3 cache and MN for each Super CPU
+cluster.
+For DRR controller separate PMU shall be registered for each channel in a
+Super CPU cluster.
+
+The available events and configuration options shall be described in the sysfs.
+The "perf list" shall list the available events from sysfs.
+eg. hisi_l3c2/read_allocate/ [kernel PMU event]
+
+The Super Cluster ID will be the number suffix to PMU name
+e.g. hisi_l3c2. Here Super cluster ID is 2 and so hisi_l3c2/read_allocate
+is the event for read_allocate of SCCL #2.
+
+For DDRC the channel number will be suffix at the end.
+eg: hisi_ddrc2_0/flux_read/. Here Super cluster ID is 2 and the channel number
+is 0 for the event flux_read.
+
+The event code is represented by 12 bits.
+ i) event 0-11
+ The event code will be represented using the LSB 12 bits.
+
+The driver also provides a "cpumask" sysfs attribute, which shows the CPU core
+ID used to count the uncore PMU event.
+
+Example usage of perf:
+$# perf list
+hisi_l3c2/read_hit/ [kernel PMU event]
+hisi_l3c2/write_hit/ [kernel PMU event]
+------------------------------------------
+hisi_l3c1/read_hit/ [kernel PMU event]
+hisi_l3c1/write_hit/ [kernel PMU event]
+------------------------------------------
+hisi_mn2/read_req/ [kernel PMU event]
+hisi_mn2/write_req/ [kernel PMU event]
+------------------------------------------
+hisi_ddrc2_0/flux_read/ [kernel PMU event]
+------------------------------------------
+
+$# perf stat -a -e hisi_l3c2/read_allocate/ sleep 5
+
+The current driver doesnot support sampling. so "perf record" is unsupported.
+Also attach to a task is unsupported as the events are all uncore.
+
+Note: Please contact the maintainer for a complete list of events supported for
+the PMU devices in the SoC and its information if needed.
--
2.1.4
^ permalink raw reply related
* [PATCH v1 03/11] drivers: soc: hisi: Add support for Hisilicon Djtag driver
From: Anurup M @ 2016-11-02 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478101374-18778-1-git-send-email-anurup.m@huawei.com>
From: Tan Xiaojun <tanxiaojun@huawei.com>
The Hisilicon Djtag is an independent component which connects
with some other components in the SoC by Debug Bus. This driver
can be configured to access the registers of connecting components
(like L3 cache) during real time debugging.
Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Anurup M <anurup.m@huawei.com>
---
drivers/soc/Kconfig | 1 +
drivers/soc/Makefile | 1 +
drivers/soc/hisilicon/Kconfig | 12 +
drivers/soc/hisilicon/Makefile | 1 +
drivers/soc/hisilicon/djtag.c | 639 ++++++++++++++++++++++++++++++++++++
include/linux/soc/hisilicon/djtag.h | 38 +++
6 files changed, 692 insertions(+)
create mode 100644 drivers/soc/hisilicon/Kconfig
create mode 100644 drivers/soc/hisilicon/Makefile
create mode 100644 drivers/soc/hisilicon/djtag.c
create mode 100644 include/linux/soc/hisilicon/djtag.h
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index e6e90e8..89ecd42 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -3,6 +3,7 @@ menu "SOC (System On Chip) specific Drivers"
source "drivers/soc/bcm/Kconfig"
source "drivers/soc/fsl/qbman/Kconfig"
source "drivers/soc/fsl/qe/Kconfig"
+source "drivers/soc/hisilicon/Kconfig"
source "drivers/soc/mediatek/Kconfig"
source "drivers/soc/qcom/Kconfig"
source "drivers/soc/rockchip/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 50c23d0..ce2beb5 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -6,6 +6,7 @@ obj-y += bcm/
obj-$(CONFIG_ARCH_DOVE) += dove/
obj-$(CONFIG_MACH_DOVE) += dove/
obj-y += fsl/
+obj-$(CONFIG_ARCH_HISI) += hisilicon/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
obj-$(CONFIG_ARCH_QCOM) += qcom/
obj-$(CONFIG_ARCH_RENESAS) += renesas/
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
new file mode 100644
index 0000000..6dd4ba0
--- /dev/null
+++ b/drivers/soc/hisilicon/Kconfig
@@ -0,0 +1,12 @@
+#
+# Hisilicon SoC drivers
+#
+config HISI_DJTAG
+ bool "Hisilicon Djtag Support"
+ depends on ARCH_HISI || COMPILE_TEST
+ help
+ Say y here to enable the Hisilicon Djtag support. It is
+ an independent component which connects with some other
+ components in the SoC by Debug Bus. This driver can be
+ configured to access the registers of connecting
+ components during real time debugging.
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
new file mode 100644
index 0000000..35a7b4b
--- /dev/null
+++ b/drivers/soc/hisilicon/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HISI_DJTAG) += djtag.o
diff --git a/drivers/soc/hisilicon/djtag.c b/drivers/soc/hisilicon/djtag.c
new file mode 100644
index 0000000..a87c8b6
--- /dev/null
+++ b/drivers/soc/hisilicon/djtag.c
@@ -0,0 +1,639 @@
+/*
+ * Driver for Hisilicon Djtag r/w via System Controller.
+ *
+ * Copyright (C) 2016 Hisilicon Ltd.
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm-generic/delay.h>
+#include <linux/soc/hisilicon/djtag.h>
+
+#define SC_DJTAG_TIMEOUT 100000 /* 100ms */
+
+/* for djtag v1 */
+#define SC_DJTAG_MSTR_EN 0x6800
+#define DJTAG_NOR_CFG BIT(1) /* accelerate R,W */
+#define DJTAG_MSTR_EN BIT(0)
+#define SC_DJTAG_MSTR_START_EN 0x6804
+#define DJTAG_MSTR_START_EN 0x1
+#define SC_DJTAG_DEBUG_MODULE_SEL 0x680c
+#define SC_DJTAG_MSTR_WR 0x6810
+#define DJTAG_MSTR_W 0x1
+#define DJTAG_MSTR_R 0x0
+#define SC_DJTAG_CHAIN_UNIT_CFG_EN 0x6814
+#define CHAIN_UNIT_CFG_EN 0xFFFF
+#define SC_DJTAG_MSTR_ADDR 0x6818
+#define SC_DJTAG_MSTR_DATA 0x681c
+#define SC_DJTAG_RD_DATA_BASE 0xe800
+
+/* for djtag v2 */
+#define SC_DJTAG_SEC_ACC_EN_EX 0xd800
+#define DJTAG_SEC_ACC_EN_EX 0x1
+#define SC_DJTAG_MSTR_CFG_EX 0xd818
+#define DJTAG_MSTR_RW_SHIFT_EX 29
+#define DJTAG_MSTR_RD_EX (0x0 << DJTAG_MSTR_RW_SHIFT_EX)
+#define DJTAG_MSTR_WR_EX (0x1 << DJTAG_MSTR_RW_SHIFT_EX)
+#define DEBUG_MODULE_SEL_SHIFT_EX 16
+#define CHAIN_UNIT_CFG_EN_EX 0xFFFF
+#define SC_DJTAG_MSTR_ADDR_EX 0xd810
+#define SC_DJTAG_MSTR_DATA_EX 0xd814
+#define SC_DJTAG_MSTR_START_EN_EX 0xd81c
+#define DJTAG_MSTR_START_EN_EX 0x1
+#define SC_DJTAG_RD_DATA_BASE_EX 0xe800
+#define SC_DJTAG_OP_ST_EX 0xe828
+#define DJTAG_OP_DONE_EX BIT(8)
+
+#define DJTAG_PREFIX "hisi-djtag-dev-"
+
+DEFINE_IDR(djtag_hosts_idr);
+
+struct hisi_djtag_host {
+ spinlock_t lock;
+ int id;
+ struct device dev;
+ struct list_head client_list;
+ void __iomem *sysctl_reg_map;
+ struct device_node *of_node;
+ int (*djtag_readwrite)(void __iomem *regs_base, u32 offset,
+ u32 mod_sel, u32 mod_mask, bool is_w,
+ u32 wval, int chain_id, u32 *rval);
+};
+
+#define to_hisi_djtag_client(d) container_of(d, struct hisi_djtag_client, dev)
+#define to_hisi_djtag_driver(d) container_of(d, struct hisi_djtag_driver, \
+ driver)
+#define MODULE_PREFIX "hisi_djtag:"
+
+static void djtag_read32_relaxed(void __iomem *regs_base, u32 off, u32 *value)
+{
+ void __iomem *reg_addr = regs_base + off;
+
+ *value = readl_relaxed(reg_addr);
+}
+
+static void djtag_write32(void __iomem *regs_base, u32 off, u32 val)
+{
+ void __iomem *reg_addr = regs_base + off;
+
+ writel(val, reg_addr);
+}
+
+/*
+ * djtag_readwrite_v1/v2: djtag read/write interface
+ * @reg_base: djtag register base address
+ * @offset: register's offset
+ * @mod_sel: module selection
+ * @mod_mask: mask to select specific modules for write
+ * @is_w: write -> true, read -> false
+ * @wval: value to register for write
+ * @chain_id: which sub module for read
+ * @rval: value in register for read
+ *
+ * Return non-zero if error, else return 0.
+ */
+static int djtag_readwrite_v1(void __iomem *regs_base, u32 offset, u32 mod_sel,
+ u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
+{
+ u32 rd;
+ int timeout = SC_DJTAG_TIMEOUT;
+
+ if (!(mod_mask & CHAIN_UNIT_CFG_EN)) {
+ pr_warn("djtag: do nothing.\n");
+ return 0;
+ }
+
+ /* djtag mster enable & accelerate R,W */
+ djtag_write32(regs_base, SC_DJTAG_MSTR_EN,
+ DJTAG_NOR_CFG | DJTAG_MSTR_EN);
+
+ /* select module */
+ djtag_write32(regs_base, SC_DJTAG_DEBUG_MODULE_SEL, mod_sel);
+ djtag_write32(regs_base, SC_DJTAG_CHAIN_UNIT_CFG_EN,
+ mod_mask & CHAIN_UNIT_CFG_EN);
+
+ if (is_w) {
+ djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_W);
+ djtag_write32(regs_base, SC_DJTAG_MSTR_DATA, wval);
+ } else
+ djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_R);
+
+ /* address offset */
+ djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR, offset);
+
+ /* start to write to djtag register */
+ djtag_write32(regs_base, SC_DJTAG_MSTR_START_EN, DJTAG_MSTR_START_EN);
+
+ /* ensure the djtag operation is done */
+ do {
+ djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN, &rd);
+ if (!(rd & DJTAG_MSTR_EN))
+ break;
+
+ udelay(1);
+ } while (timeout--);
+
+ if (timeout < 0) {
+ pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
+ return -EBUSY;
+ }
+
+ if (!is_w)
+ djtag_read32_relaxed(regs_base,
+ SC_DJTAG_RD_DATA_BASE + chain_id * 0x4, rval);
+
+ return 0;
+}
+
+static int djtag_readwrite_v2(void __iomem *regs_base, u32 offset, u32 mod_sel,
+ u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
+{
+ u32 rd;
+ int timeout = SC_DJTAG_TIMEOUT;
+
+ if (!(mod_mask & CHAIN_UNIT_CFG_EN_EX)) {
+ pr_warn("djtag: do nothing.\n");
+ return 0;
+ }
+
+ /* djtag mster enable */
+ djtag_write32(regs_base, SC_DJTAG_SEC_ACC_EN_EX, DJTAG_SEC_ACC_EN_EX);
+
+ if (is_w) {
+ djtag_write32(regs_base, SC_DJTAG_MSTR_CFG_EX, DJTAG_MSTR_WR_EX
+ | (mod_sel << DEBUG_MODULE_SEL_SHIFT_EX)
+ | (mod_mask & CHAIN_UNIT_CFG_EN_EX));
+ djtag_write32(regs_base, SC_DJTAG_MSTR_DATA_EX, wval);
+ } else
+ djtag_write32(regs_base, SC_DJTAG_MSTR_CFG_EX, DJTAG_MSTR_RD_EX
+ | (mod_sel << DEBUG_MODULE_SEL_SHIFT_EX)
+ | (mod_mask & CHAIN_UNIT_CFG_EN_EX));
+
+ /* address offset */
+ djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR_EX, offset);
+
+ /* start to write to djtag register */
+ djtag_write32(regs_base,
+ SC_DJTAG_MSTR_START_EN_EX, DJTAG_MSTR_START_EN_EX);
+
+ /* ensure the djtag operation is done */
+ do {
+ djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN_EX, &rd);
+
+ if (!(rd & DJTAG_MSTR_START_EN_EX))
+ break;
+
+ udelay(1);
+ } while (timeout--);
+
+ if (timeout < 0)
+ goto timeout;
+
+ timeout = SC_DJTAG_TIMEOUT;
+ do {
+ djtag_read32_relaxed(regs_base, SC_DJTAG_OP_ST_EX, &rd);
+
+ if (rd & DJTAG_OP_DONE_EX)
+ break;
+
+ udelay(1);
+ } while (timeout--);
+
+ if (timeout < 0)
+ goto timeout;
+
+ if (!is_w)
+ djtag_read32_relaxed(regs_base,
+ SC_DJTAG_RD_DATA_BASE_EX + chain_id * 0x4,
+ rval);
+
+ return 0;
+
+timeout:
+ pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
+ return -EBUSY;
+}
+
+
+/**
+ * djtag_writel - write registers via djtag
+ * @node: djtag node
+ * @offset: register's offset
+ * @mod_sel: module selection
+ * @mod_mask: mask to select specific modules
+ * @val: value to write to register
+ *
+ * If error return errno, otherwise return 0.
+ */
+int hisi_djtag_writel(struct hisi_djtag_client *client, u32 offset, u32 mod_sel,
+ u32 mod_mask, u32 val)
+{
+ void __iomem *reg_map = client->host->sysctl_reg_map;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&client->host->lock, flags);
+ ret = client->host->djtag_readwrite(reg_map, offset, mod_sel, mod_mask,
+ true, val, 0, NULL);
+ if (ret)
+ pr_err("djtag_writel: error! ret=%d\n", ret);
+ spin_unlock_irqrestore(&client->host->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_djtag_writel);
+
+/**
+ * djtag_readl - read registers via djtag
+ * @node: djtag node
+ * @offset: register's offset
+ * @mod_sel: module type selection
+ * @chain_id: chain_id number, mostly is 0
+ * @val: register's value
+ *
+ * If error return errno, otherwise return 0.
+ */
+int hisi_djtag_readl(struct hisi_djtag_client *client, u32 offset, u32 mod_sel,
+ int chain_id, u32 *val)
+{
+ void __iomem *reg_map = client->host->sysctl_reg_map;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&client->host->lock, flags);
+ ret = client->host->djtag_readwrite(reg_map, offset, mod_sel,
+ 0xffff, false, 0, chain_id, val);
+ if (ret)
+ pr_err("djtag_readl: error! ret=%d\n", ret);
+ spin_unlock_irqrestore(&client->host->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_djtag_readl);
+
+static const struct of_device_id djtag_of_match[] = {
+ /* for hip05(D02) cpu die */
+ { .compatible = "hisilicon,hip05-cpu-djtag-v1",
+ .data = (void *)djtag_readwrite_v1 },
+ /* for hip05(D02) io die */
+ { .compatible = "hisilicon,hip05-io-djtag-v1",
+ .data = (void *)djtag_readwrite_v1 },
+ /* for hip06(D03) cpu die */
+ { .compatible = "hisilicon,hip06-cpu-djtag-v1",
+ .data = (void *)djtag_readwrite_v1 },
+ /* for hip06(D03) io die */
+ { .compatible = "hisilicon,hip06-io-djtag-v2",
+ .data = (void *)djtag_readwrite_v2 },
+ /* for hip07(D05) cpu die */
+ { .compatible = "hisilicon,hip07-cpu-djtag-v2",
+ .data = (void *)djtag_readwrite_v2 },
+ /* for hip07(D05) io die */
+ { .compatible = "hisilicon,hip07-io-djtag-v2",
+ .data = (void *)djtag_readwrite_v2 },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, djtag_of_match);
+
+static ssize_t
+show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct hisi_djtag_client *client = to_hisi_djtag_client(dev);
+
+ return sprintf(buf, "%s%s\n", MODULE_PREFIX, client->name);
+}
+static DEVICE_ATTR(modalias, 0444, show_modalias, NULL);
+
+static struct attribute *hisi_djtag_dev_attrs[] = {
+ NULL,
+ /* modalias helps coldplug: modprobe $(cat .../modalias) */
+ &dev_attr_modalias.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(hisi_djtag_dev);
+
+static struct device_type hisi_djtag_client_type = {
+ .groups = hisi_djtag_dev_groups,
+};
+
+struct hisi_djtag_client *hisi_djtag_verify_client(struct device *dev)
+{
+ return (dev->type == &hisi_djtag_client_type)
+ ? to_hisi_djtag_client(dev)
+ : NULL;
+}
+
+static int hisi_djtag_device_probe(struct device *dev)
+{
+ struct hisi_djtag_driver *driver;
+ struct hisi_djtag_client *client;
+ int rc;
+
+ client = hisi_djtag_verify_client(dev);
+ if (!client) {
+ dev_err(dev, "could not find client\n");
+ return -ENODEV;
+ }
+
+ driver = to_hisi_djtag_driver(dev->driver);
+ if (!driver) {
+ dev_err(dev, "could not find driver\n");
+ return -ENODEV;
+ }
+
+ rc = driver->probe(client);
+ if (rc < 0) {
+ dev_err(dev, "client probe failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int hisi_djtag_device_remove(struct device *dev)
+{
+ struct hisi_djtag_driver *driver;
+ struct hisi_djtag_client *client;
+ int rc;
+
+ client = hisi_djtag_verify_client(dev);
+ if (!client) {
+ dev_err(dev, "could not find client\n");
+ return -ENODEV;
+ }
+
+ driver = to_hisi_djtag_driver(dev->driver);
+ if (!driver) {
+ dev_err(dev, "could not find driver\n");
+ return -ENODEV;
+ }
+
+ rc = driver->remove(client);
+ if (rc < 0) {
+ dev_err(dev, "client probe failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int hisi_djtag_device_match(struct device *dev,
+ struct device_driver *drv)
+{
+ const struct of_device_id *p;
+ struct hisi_djtag_client *client = hisi_djtag_verify_client(dev);
+
+ if (!client)
+ return false;
+
+ if (of_driver_match_device(dev, drv))
+ return true;
+
+ p = of_match_device(drv->of_match_table, dev);
+ if (!p)
+ return false;
+
+ return true;
+}
+
+struct bus_type hisi_djtag_bus = {
+ .name = "hisi-djtag",
+ .match = hisi_djtag_device_match,
+ .probe = hisi_djtag_device_probe,
+ .remove = hisi_djtag_device_remove,
+};
+
+struct hisi_djtag_client *hisi_djtag_new_device(struct hisi_djtag_host *host,
+ struct device_node *node)
+{
+ struct hisi_djtag_client *client;
+ int status;
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return NULL;
+
+ client->host = host;
+
+ client->dev.parent = &client->host->dev;
+ client->dev.bus = &hisi_djtag_bus;
+ client->dev.type = &hisi_djtag_client_type;
+ client->dev.of_node = node;
+ snprintf(client->name, DJTAG_CLIENT_NAME_LEN, "%s%s",
+ DJTAG_PREFIX, node->name);
+ dev_set_name(&client->dev, "%s", client->name);
+
+ status = device_register(&client->dev);
+ if (status < 0) {
+ pr_err("error adding new device, status=%d\n", status);
+ kfree(client);
+ return NULL;
+ }
+
+ return client;
+}
+
+static struct hisi_djtag_client *hisi_djtag_of_register_device(
+ struct hisi_djtag_host *host,
+ struct device_node *node)
+{
+ struct hisi_djtag_client *client;
+
+ client = hisi_djtag_new_device(host, node);
+ if (client == NULL) {
+ dev_err(&host->dev, "error registering device %s\n",
+ node->full_name);
+ of_node_put(node);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return client;
+}
+
+static void djtag_register_devices(struct hisi_djtag_host *host)
+{
+ struct device_node *node;
+ struct hisi_djtag_client *client;
+
+ if (!host->of_node)
+ return;
+
+ for_each_available_child_of_node(host->of_node, node) {
+ if (of_node_test_and_set_flag(node, OF_POPULATED))
+ continue;
+ client = hisi_djtag_of_register_device(host, node);
+ list_add(&client->next, &host->client_list);
+ }
+}
+
+static int hisi_djtag_add_host(struct hisi_djtag_host *host)
+{
+ int rc;
+
+ host->dev.bus = &hisi_djtag_bus;
+
+ rc = idr_alloc(&djtag_hosts_idr, host, 0, 0, GFP_KERNEL);
+ if (rc < 0) {
+ dev_err(&host->dev, "No available djtag host ID'!s\n");
+ return rc;
+ }
+ host->id = rc;
+
+ /* Suffix the unique ID and set djtag hostname */
+ dev_set_name(&host->dev, "djtag-host-%d", host->id);
+
+ rc = device_register(&host->dev);
+ if (rc < 0) {
+ dev_err(&host->dev, "add_host dev register failed, rc=%d\n",
+ rc);
+ idr_remove(&djtag_hosts_idr, host->id);
+ return rc;
+ }
+
+ djtag_register_devices(host);
+
+ return 0;
+}
+
+static int djtag_host_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct hisi_djtag_host *host;
+ const struct of_device_id *of_id;
+ struct resource *res;
+ int rc;
+
+ of_id = of_match_device(djtag_of_match, dev);
+ if (!of_id)
+ return -EINVAL;
+
+ host = kzalloc(sizeof(*host), GFP_KERNEL);
+ if (!host)
+ return -ENOMEM;
+
+ host->of_node = dev->of_node;
+ host->djtag_readwrite = of_id->data;
+ spin_lock_init(&host->lock);
+
+ INIT_LIST_HEAD(&host->client_list);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "No reg resorces!\n");
+ kfree(host);
+ return -EINVAL;
+ }
+
+ if (!resource_size(res)) {
+ dev_err(&pdev->dev, "Zero reg entry!\n");
+ kfree(host);
+ return -EINVAL;
+ }
+
+ host->sysctl_reg_map = devm_ioremap_resource(dev, res);
+ if (IS_ERR(host->sysctl_reg_map)) {
+ dev_warn(dev, "Unable to map sysctl registers.\n");
+ kfree(host);
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, host);
+
+ rc = hisi_djtag_add_host(host);
+ if (rc) {
+ dev_err(dev, "add host failed, rc=%d\n", rc);
+ kfree(host);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int djtag_host_remove(struct platform_device *pdev)
+{
+ struct hisi_djtag_host *host;
+ struct hisi_djtag_client *client, *tmp;
+ struct list_head *client_list;
+
+ host = platform_get_drvdata(pdev);
+ client_list = &host->client_list;
+
+ list_for_each_entry_safe(client, tmp, client_list, next) {
+ list_del(&client->next);
+ kfree(client);
+ }
+
+ device_unregister(&host->dev);
+ idr_remove(&djtag_hosts_idr, host->id);
+ kfree(host);
+
+ return 0;
+}
+
+static struct platform_driver djtag_dev_driver = {
+ .driver = {
+ .name = "hisi-djtag",
+ .of_match_table = djtag_of_match,
+ },
+ .probe = djtag_host_probe,
+ .remove = djtag_host_remove,
+};
+module_platform_driver(djtag_dev_driver);
+
+int hisi_djtag_register_driver(struct module *owner,
+ struct hisi_djtag_driver *driver)
+{
+ int rc;
+
+ driver->driver.owner = owner;
+ driver->driver.bus = &hisi_djtag_bus;
+
+ rc = driver_register(&driver->driver);
+ if (rc < 0)
+ pr_err("%s register failed, rc=%d\n", __func__, rc);
+
+ return rc;
+}
+
+void hisi_djtag_unregister_driver(struct hisi_djtag_driver *driver)
+{
+ driver->driver.bus = &hisi_djtag_bus;
+ driver_unregister(&driver->driver);
+}
+
+static int __init hisi_djtag_init(void)
+{
+ int rc;
+
+ rc = bus_register(&hisi_djtag_bus);
+ if (rc) {
+ pr_err("hisi djtag init failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+module_init(hisi_djtag_init);
+
+static void __exit hisi_djtag_exit(void)
+{
+ bus_unregister(&hisi_djtag_bus);
+}
+module_exit(hisi_djtag_exit);
+
+MODULE_DESCRIPTION("Hisilicon djtag driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
diff --git a/include/linux/soc/hisilicon/djtag.h b/include/linux/soc/hisilicon/djtag.h
new file mode 100644
index 0000000..aae3bca
--- /dev/null
+++ b/include/linux/soc/hisilicon/djtag.h
@@ -0,0 +1,38 @@
+/*
+ * Driver for Hisilicon djtag r/w via System Controller.
+ *
+ * Copyright (C) 2016-2017 Hisilicon Ltd.
+ *
+ * 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.
+ */
+
+#ifndef __HISI_DJTAG_H
+#define __HISI_DJTAG_H
+
+#define DJTAG_CLIENT_NAME_LEN 24
+
+struct hisi_djtag_client {
+ struct hisi_djtag_host *host;
+ struct list_head next;
+ char name[DJTAG_CLIENT_NAME_LEN];
+ struct device dev;
+};
+
+struct hisi_djtag_driver {
+ struct device_driver driver;
+ int (*probe)(struct hisi_djtag_client *);
+ int (*remove)(struct hisi_djtag_client *);
+};
+
+extern struct bus_type hisi_djtag_bus;
+
+int hisi_djtag_register_driver(struct module *owner,
+ struct hisi_djtag_driver *driver);
+void hisi_djtag_unregister_driver(struct hisi_djtag_driver *driver);
+int hisi_djtag_readl(struct hisi_djtag_client *client, u32 offset,
+ u32 mod_sel, int chain_id, u32 *val);
+int hisi_djtag_writel(struct hisi_djtag_client *client, u32 offset,
+ u32 mod_sel, u32 mod_mask, u32 val);
+#endif /* __HISI_DJTAG_H */
--
2.1.4
^ permalink raw reply related
* [PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings
From: Anurup M @ 2016-11-02 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478101374-18778-1-git-send-email-anurup.m@huawei.com>
From: Tan Xiaojun <tanxiaojun@huawei.com>
1) Add Hisilicon HiP05/06/07 CPU and ALGSUB system controller dts
bindings.
2) Add Hisilicon Djtag dts binding.
Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
Signed-off-by: Anurup M <anurup.m@huawei.com>
---
.../bindings/arm/hisilicon/hisilicon.txt | 82 ++++++++++++++++++++++
1 file changed, 82 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
index 7df79a7..341cbb9 100644
--- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
+++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
@@ -270,3 +270,85 @@ Required Properties:
[1]: bootwrapper size
[2]: relocation physical address
[3]: relocation size
+
+-----------------------------------------------------------------------
+The Hisilicon Djtag in CPU die is an independent component which connects with
+some other components in the SoC by Debug Bus. This driver can be configured
+to access the registers of connecting components (like L3 cache, l3 cache PMU
+ etc.) during real time debugging by sysctrl. These components appear as child
+nodes of djtag.
+
+The Hip05/06/07 CPU system controller(sysctrl) support to manage some important
+components (such as clock, reset, soft reset, secure debugger, etc.).
+The CPU sysctrl registers in hip05/06/07 doesnot use syscon but will be mapped
+by djtag driver for use by connecting components.
+
+Hisilicon HiP05 CPU system controller
+Required properties:
+ - compatible : "hisilicon,hip05-cpu-djtag-v1"
+ - reg : Register address and size
+
+Hisilicon HiP06 djtag for CPU sysctrl
+Required properties:
+- compatible : "hisilicon,hip06-sysctrl", "syscon", "simple-mfd";
+- reg : Register address and size
+- djtag :
+ - compatible : "hisilicon,hip06-cpu-djtag-v1"
+ - reg : Register address and size
+
+Hisilicon HiP07 djtag for CPU sysctrl
+Required properties:
+ - compatible : "hisilicon,hip07-cpu-djtag-v2"
+ - reg : Register address and size
+
+Example:
+ /* for Hisilicon HiP05 djtag for CPU sysctrl */
+ djtag0: djtag at 80010000 {
+ compatible = "hisilicon,hip05-cpu-djtag-v1";
+ reg = <0x0 0x80010000 0x0 0x10000>;
+
+ /* For L3 cache PMU */
+ pmul3c0 {
+ compatible = "hisilicon,hisi-pmu-l3c-v1";
+ scl-id = <0x02>;
+ num-events = <0x16>;
+ num-counters = <0x08>;
+ module-id = <0x04>;
+ num-banks = <0x04>;
+ cfgen-map = <0x02 0x04 0x01 0x08>;
+ counter-reg = <0x170>;
+ evctrl-reg = <0x04>;
+ event-en = <0x1000000>;
+ evtype-reg = <0x140>;
+ };
+ };
+
+-----------------------------------------------------------------------
+The Hisilicon HiP05/06/07 ALGSUB system controller(sysctrl) is in IO die
+of SoC. It has a similar function as the Hisilicon HiP05/06/07 CPU system
+controller in CPU die and it manage different components, like RSA, etc.
+The Hisilicon Djtag in IO die has a similar function as in CPU die and maps
+the sysctrl registers for use by connecting components.
+All connecting components shall appear as child nodes of djtag.
+
+Hisilicon HiP05 djtag for ALGSUB sysctrl
+Required properties:
+ - compatible : "hisilicon,hip05-io-djtag-v1"
+ - reg : Register address and size
+
+Hisilicon HiP06 djtag for ALGSUB sysctrl
+Required properties:
+ - compatible : "hisilicon,hip06-io-djtag-v2"
+ - reg : Register address and size
+
+Hisilicon HiP07 djtag for ALGSUB sysctrl
+Required properties:
+ - compatible : "hisilicon,hip07-io-djtag-v2"
+ - reg : Register address and size
+
+Example:
+ /* for Hisilicon HiP05 djtag for alg sysctrl */
+ djtag0: djtag at d0000000 {
+ compatible = "hisilicon,hip05-io-djtag-v1";
+ reg = <0x0 0xd0000000 0x0 0x10000>;
+ };
--
2.1.4
^ permalink raw reply related
* [PATCH v1 01/11] arm64: MAINTAINERS: hisi: Add hisilicon SoC PMU support
From: Anurup M @ 2016-11-02 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478101374-18778-1-git-send-email-anurup.m@huawei.com>
Add support for Hisilicon SoC hardware event counters
for HIP05/06/07 chip versions.
Signed-off-by: Anurup M <anurup.m@huawei.com>
---
MAINTAINERS | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index b224caa..839abc8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5725,6 +5725,16 @@ S: Maintained
F: drivers/net/ethernet/hisilicon/
F: Documentation/devicetree/bindings/net/hisilicon*.txt
+HISILICON SOC PMU
+M: Anurup M <anurup.m@huawei.com>
+W: http://www.hisilicon.com
+S: Supported
+F: drivers/perf/hisilicon/*
+F: drivers/soc/hisilicon/djtag.c
+F: include/linux/soc/hisilicon/djtag.h
+F: Documentation/perf/hisi-pmu.txt
+F: Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
+
HISILICON ROCE DRIVER
M: Lijun Ou <oulijun@huawei.com>
M: Wei Hu(Xavier) <xavier.huwei@huawei.com>
--
2.1.4
^ permalink raw reply related
* [PATCH v1 00/11] perf: arm64: Support for Hisilicon SoC Hardware event counters
From: Anurup M @ 2016-11-02 15:42 UTC (permalink / raw)
To: linux-arm-kernel
Provide Support for Hisilicon SoC(Hip05/06/07) Hardware event counters.
The Hisilicon SoC Hip0x series has many uncore or non-CPU performance
events and counters units.
This initial patch series is implemented refering to arm-cci, Intel/AMD uncore and
also the cavium thunderX and xgene uncore pmu patches.
Support for Hisilicon L3 cache(L3C), MN and DDR hardware events and
counters are added in this implementation.
The Hisilicon uncore PMUs can be found under /sys/bus/event_source/devices.
The counters are exported via sysfs in the corresponding events files
under the PMU directory so the perf tool can list the event names.
ToDo:
1) The counter overflow handling is currently unsupported in this
patch series.
2) ACPI support.
Anurup M (8):
arm64: MAINTAINERS: hisi: Add hisilicon SoC PMU support
Documentation: perf: hisi: Documentation for HIP05/06/07 PMU event
counting.
dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
perf: hisi: Update Kconfig for Hisilicon PMU support
perf: hisi: Add support for Hisilicon SoC event counters
perf: hisi: Add sysfs attributes for L3 cache(L3C) PMU
perf: hisi: Support for Hisilicon DDRC PMU.
dts: arm64: hip06: Add Hisilicon SoC PMU support
Shaokun Zhang (1):
perf: hisi: Miscellanous node(MN) event counting in perf
Tan Xiaojun (2):
dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts
bindings
drivers: soc: hisi: Add support for Hisilicon Djtag driver
.../bindings/arm/hisilicon/hisilicon.txt | 82 +++
.../devicetree/bindings/arm/hisilicon/pmu.txt | 127 ++++
Documentation/perf/hisi-pmu.txt | 80 +++
MAINTAINERS | 10 +
arch/arm64/boot/dts/hisilicon/hip06.dtsi | 116 ++++
drivers/perf/Kconfig | 9 +
drivers/perf/Makefile | 1 +
drivers/perf/hisilicon/Makefile | 1 +
drivers/perf/hisilicon/hisi_uncore_ddrc.c | 444 ++++++++++++++
drivers/perf/hisilicon/hisi_uncore_ddrc.h | 73 +++
drivers/perf/hisilicon/hisi_uncore_l3c.c | 628 ++++++++++++++++++++
drivers/perf/hisilicon/hisi_uncore_l3c.h | 67 +++
drivers/perf/hisilicon/hisi_uncore_mn.c | 571 ++++++++++++++++++
drivers/perf/hisilicon/hisi_uncore_mn.h | 68 +++
drivers/perf/hisilicon/hisi_uncore_pmu.c | 371 ++++++++++++
drivers/perf/hisilicon/hisi_uncore_pmu.h | 130 +++++
drivers/soc/Kconfig | 1 +
drivers/soc/Makefile | 1 +
drivers/soc/hisilicon/Kconfig | 12 +
drivers/soc/hisilicon/Makefile | 1 +
drivers/soc/hisilicon/djtag.c | 639 +++++++++++++++++++++
include/linux/soc/hisilicon/djtag.h | 38 ++
22 files changed, 3470 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
create mode 100644 Documentation/perf/hisi-pmu.txt
create mode 100644 drivers/perf/hisilicon/Makefile
create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.c
create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.h
create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c.c
create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c.h
create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.c
create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.h
create mode 100644 drivers/perf/hisilicon/hisi_uncore_pmu.c
create mode 100644 drivers/perf/hisilicon/hisi_uncore_pmu.h
create mode 100644 drivers/soc/hisilicon/Kconfig
create mode 100644 drivers/soc/hisilicon/Makefile
create mode 100644 drivers/soc/hisilicon/djtag.c
create mode 100644 include/linux/soc/hisilicon/djtag.h
--
2.1.4
^ permalink raw reply
* [PATCH 1/2] ARM: imx: mmdc perf function support i.MX6QP
From: Zhi Li @ 2016-11-02 15:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161102144008.GC19126@dragon>
On Wed, Nov 2, 2016 at 9:40 AM, Shawn Guo <shawnguo@kernel.org> wrote:
> On Tue, Oct 25, 2016 at 04:26:56PM -0500, Frank Li wrote:
>> i.MX6QP added new reigster bit PROFILE_SEL in MADPCR0.
>> need set it at perf start.
>>
>> Signed-off-by: Frank Li <Frank.Li@nxp.com>
>> ---
>> arch/arm/mach-imx/mmdc.c | 45 +++++++++++++++++++++++++++++++++++++++------
>> 1 file changed, 39 insertions(+), 6 deletions(-)
>>
>> diff --git a/arch/arm/mach-imx/mmdc.c b/arch/arm/mach-imx/mmdc.c
>> index d82d14c..d833b87 100644
>> --- a/arch/arm/mach-imx/mmdc.c
>> +++ b/arch/arm/mach-imx/mmdc.c
>> @@ -44,6 +44,7 @@
>> #define DBG_RST 0x2
>> #define PRF_FRZ 0x4
>> #define CYC_OVF 0x8
>> +#define PROFILE_SEL 0x10
>>
>> #define MMDC_MADPCR0 0x410
>> #define MMDC_MADPSR0 0x418
>> @@ -55,10 +56,36 @@
>>
>> #define MMDC_NUM_COUNTERS 6
>>
>> +#define FSL_MMDC_QUIRK_PROFILE_SEL 0x1
>> +
>> #define to_mmdc_pmu(p) container_of(p, struct mmdc_pmu, pmu)
>>
>> static int ddr_type;
>>
>> +enum fsl_mmdc_devtype {
>> + FSL_MMDC_IMX6Q,
>> + FSL_MMDC_IMX6QP,
>> +};
>> +
>> +struct fsl_mmdc_devtype_data {
>> + enum fsl_mmdc_devtype devtype;
>> + int driver_data;
>> +};
>> +
>> +static struct fsl_mmdc_devtype_data imx6q_data = {
>> + .devtype = FSL_MMDC_IMX6Q,
>> +};
>> +
>> +static struct fsl_mmdc_devtype_data imx6qp_data = {
>> + .driver_data = FSL_MMDC_QUIRK_PROFILE_SEL,
>> +};
>> +
>> +static const struct of_device_id imx_mmdc_dt_ids[] = {
>> + { .compatible = "fsl,imx6q-mmdc", .data = (void *)&imx6q_data},
>> + { .compatible = "fsl,imx6qp-mmdc", .data = (void *)&imx6qp_data},
>> + { /* sentinel */ }
>> +};
>> +
>> #ifdef CONFIG_PERF_EVENTS
>>
>> static DEFINE_IDA(mmdc_ida);
>> @@ -83,6 +110,7 @@ struct mmdc_pmu {
>> struct device *dev;
>> struct perf_event *mmdc_events[MMDC_NUM_COUNTERS];
>> struct hlist_node node;
>> + struct fsl_mmdc_devtype_data *devtype_data;
>> };
>>
>> /*
>> @@ -307,6 +335,7 @@ static void mmdc_pmu_event_start(struct perf_event *event, int flags)
>> struct mmdc_pmu *pmu_mmdc = to_mmdc_pmu(event->pmu);
>> struct hw_perf_event *hwc = &event->hw;
>> void __iomem *mmdc_base, *reg;
>> + int val;
>>
>> mmdc_base = pmu_mmdc->mmdc_base;
>> reg = mmdc_base + MMDC_MADPCR0;
>> @@ -321,7 +350,12 @@ static void mmdc_pmu_event_start(struct perf_event *event, int flags)
>> local64_set(&hwc->prev_count, 0);
>>
>> writel(DBG_RST, reg);
>> - writel(DBG_EN, reg);
>> +
>> + val = DBG_EN;
>> + if (pmu_mmdc->devtype_data->driver_data & FSL_MMDC_QUIRK_PROFILE_SEL)
>
> Shouldn't it be good enough to have the flag telling different
> programming model between variants? That said, I do not see the point
> of introducing enum fsl_mmdc_devtype.
Okay, I can remove enum fsl_mmdc_devtype
best regards
Frank Li
>
> Shawn
>
>> + val |= PROFILE_SEL;
>> +
>> + writel(val, reg);
>> }
>>
>> static int mmdc_pmu_event_add(struct perf_event *event, int flags)
>> @@ -436,6 +470,8 @@ static int imx_mmdc_perf_init(struct platform_device *pdev, void __iomem *mmdc_b
>> char *name;
>> int mmdc_num;
>> int ret;
>> + const struct of_device_id *of_id =
>> + of_match_device(imx_mmdc_dt_ids, &pdev->dev);
>>
>> pmu_mmdc = kzalloc(sizeof(*pmu_mmdc), GFP_KERNEL);
>> if (!pmu_mmdc) {
>> @@ -450,6 +486,8 @@ static int imx_mmdc_perf_init(struct platform_device *pdev, void __iomem *mmdc_b
>> name = devm_kasprintf(&pdev->dev,
>> GFP_KERNEL, "mmdc%d", mmdc_num);
>>
>> + pmu_mmdc->devtype_data = (struct fsl_mmdc_devtype_data *)of_id->data;
>> +
>> hrtimer_init(&pmu_mmdc->hrtimer, CLOCK_MONOTONIC,
>> HRTIMER_MODE_REL);
>> pmu_mmdc->hrtimer.function = mmdc_pmu_timer_handler;
>> @@ -524,11 +562,6 @@ int imx_mmdc_get_ddr_type(void)
>> return ddr_type;
>> }
>>
>> -static const struct of_device_id imx_mmdc_dt_ids[] = {
>> - { .compatible = "fsl,imx6q-mmdc", },
>> - { /* sentinel */ }
>> -};
>> -
>> static struct platform_driver imx_mmdc_driver = {
>> .driver = {
>> .name = "imx-mmdc",
>> --
>> 2.5.2
>>
^ 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