Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 2/7] iommu/arm-smmu-v3: Do resource size checks based on smmu option PAGE0_REGS_ONLY
From: Sunil Kovvuri @ 2017-04-11 16:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <802c0a38-f5a6-e584-5795-42d8d3fd7603@arm.com>

On Tue, Apr 11, 2017 at 9:13 PM, Robin Murphy <robin.murphy@arm.com> wrote:
> On 11/04/17 15:42, linucherian at gmail.com wrote:
>> From: Linu Cherian <linu.cherian@cavium.com>
>>
>> With implementations supporting only page 0 of register space,
>> resource size can be 64k as well and hence perform size checks
>> based on smmu option PAGE0_REGS_ONLY.
>
> What harm comes of mapping page 1 if we don't access it?
>
> Robin.
>

There are multiple SMMUs on the silicon and CSRs of each SMMU are
64K apart. Hence can't map page-1 even though it's not accessed.

Thanks,
Sunil.

^ permalink raw reply

* [RFC PATCH 3/7] iommu/arm-smmu-v3: Introduce smmu option USE_SHARED_IRQS for Silicon errata
From: Robin Murphy @ 2017-04-11 16:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170411162123.GF17109@arm.com>

On 11/04/17 17:21, Will Deacon wrote:
> On Tue, Apr 11, 2017 at 04:54:26PM +0100, Robin Murphy wrote:
>> On 11/04/17 15:42, linucherian at gmail.com wrote:
>>> From: Geetha <gakula@cavium.com>
>>>
>>> Cavium 99xx SMMU implementation doesn't not support unique irq lines for
>>> gerror, eventq and cmdq-sync. USE_SHARED_IRQS option enables to use single
>>> irq line for all three interrupts.
>>
>> AFAICS, there's nothing actually wrong with using shared wired IRQs -
>> the architecture spec doesn't appear to say anything about it. I think
>> it might suffice to simply add IRQF_SHARED if we can see the SMMU
>> doesn't support MSIs anyway - it doesn't really seem like something we
>> need to treat as a specific quirk.
> 
> No, this is not permitted by the spec. See 3.18.2 ("Interrupt sources"),
> where it's clear that each source asserts a *unique* wired interrupt.

Perhaps I'm reading it too generously; it does indeed specify that the
*implementation* has to provide a unique output for each source, but
other than suggesting a particular mode of operation based on that I
don't see anything actually forbidding the *integration* from then just
munging those lines together externally, as integrators so often like to
do. That's the case I had in mind.

Robin.

> 
> Geetha: does your implementation support MSIs?
> 
> Will
> 

^ permalink raw reply

* [RFC PATCH 3/7] iommu/arm-smmu-v3: Introduce smmu option USE_SHARED_IRQS for Silicon errata
From: Sunil Kovvuri @ 2017-04-11 16:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170411162123.GF17109@arm.com>

On Tue, Apr 11, 2017 at 9:51 PM, Will Deacon <will.deacon@arm.com> wrote:
> On Tue, Apr 11, 2017 at 04:54:26PM +0100, Robin Murphy wrote:
>> On 11/04/17 15:42, linucherian at gmail.com wrote:
>> > From: Geetha <gakula@cavium.com>
>> >
>> > Cavium 99xx SMMU implementation doesn't not support unique irq lines for
>> > gerror, eventq and cmdq-sync. USE_SHARED_IRQS option enables to use single
>> > irq line for all three interrupts.
>>
>> AFAICS, there's nothing actually wrong with using shared wired IRQs -
>> the architecture spec doesn't appear to say anything about it. I think
>> it might suffice to simply add IRQF_SHARED if we can see the SMMU
>> doesn't support MSIs anyway - it doesn't really seem like something we
>> need to treat as a specific quirk.
>
> No, this is not permitted by the spec. See 3.18.2 ("Interrupt sources"),
> where it's clear that each source asserts a *unique* wired interrupt.
>
> Geetha: does your implementation support MSIs?
>
> Will

No, this silicon doesn't support MSIs.

Thanks,
Sunil.

^ permalink raw reply

* [PATCH 2/2] arm64: acpi/pci: invoke _DSM whether to preserve firmware PCI setup
From: Ard Biesheuvel @ 2017-04-11 16:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170411163313.18577-1-ard.biesheuvel@linaro.org>

On arm64 ACPI systems, we unconditionally reconfigure the entire PCI
hierarchy at boot. This is a departure from what is customary on ACPI
systems, and may break assumptions in some places (e.g., EFIFB), that
the kernel will leave BARs of enabled PCI devices where they are.

Given that PCI already specifies a device specific ACPI method (_DSM)
for PCI root bridge nodes that tells us whether the firmware thinks
the configuration should be left alone, let's sidestep the entire
policy debate about whether the PCI configuration should be preserved
or not, and put it under the control of the firmware instead.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 arch/arm64/kernel/pci.c  | 20 ++++++++++++++++++--
 include/linux/pci-acpi.h |  7 ++++---
 2 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
index 4f0e3ebfea4b..c88e07e2eb84 100644
--- a/arch/arm64/kernel/pci.c
+++ b/arch/arm64/kernel/pci.c
@@ -185,6 +185,7 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
 	struct acpi_pci_generic_root_info *ri;
 	struct pci_bus *bus, *child;
 	struct acpi_pci_root_ops *root_ops;
+	union acpi_object *obj;
 
 	ri = kzalloc_node(sizeof(*ri), GFP_KERNEL, node);
 	if (!ri)
@@ -208,8 +209,23 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
 	if (!bus)
 		return NULL;
 
-	pci_bus_size_bridges(bus);
-	pci_bus_assign_resources(bus);
+	/*
+	 * Invoke the PCI device specific method (_DSM) #5 'Ignore PCI Boot
+	 * Configuration', which tells us whether the firmware wants us to
+	 * preserve the configuration of the PCI resource tree for this root
+	 * bridge.
+	 */
+	obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), pci_acpi_dsm_uuid, 1,
+				IGNORE_PCI_BOOT_CONFIG_DSM, NULL);
+	if (obj && obj->type == ACPI_TYPE_INTEGER && obj->integer.value == 0) {
+		/* preserve existing resource assignment */
+		pci_bus_claim_resources(bus);
+	} else {
+		/* reconfigure the resource tree from scratch */
+		pci_bus_size_bridges(bus);
+		pci_bus_assign_resources(bus);
+	}
+	ACPI_FREE(obj);
 
 	list_for_each_entry(child, &bus->children, node)
 		pcie_bus_configure_settings(child);
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index 7a4e83a8c89c..308111489b83 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -106,9 +106,10 @@ static inline void acpiphp_check_host_bridge(struct acpi_device *adev) { }
 #endif
 
 extern const u8 pci_acpi_dsm_uuid[];
-#define DEVICE_LABEL_DSM	0x07
-#define RESET_DELAY_DSM		0x08
-#define FUNCTION_DELAY_DSM	0x09
+#define IGNORE_PCI_BOOT_CONFIG_DSM	0x05
+#define DEVICE_LABEL_DSM		0x07
+#define RESET_DELAY_DSM			0x08
+#define FUNCTION_DELAY_DSM		0x09
 
 #else	/* CONFIG_ACPI */
 static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
-- 
2.9.3

^ permalink raw reply related

* [PATCH 1/2] drivers: pci: do not disregard parent resources starting at 0x0
From: Ard Biesheuvel @ 2017-04-11 16:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170411163313.18577-1-ard.biesheuvel@linaro.org>

Commit f44116ae8818 ("PCI: Remove pci_find_parent_resource() use for
allocation") updated the logic that iterates over all bus resources
and compares them to a given resource, in order to decide whether one
is the parent of the latter.

This change inadvertently causes pci_find_parent_resource() to disregard
resources starting at address 0x0, resulting in an error such as the one
below on ARM systems whose I/O window starts at 0x0.

pci_bus 0000:00: root bus resource [mem 0x10000000-0x3efeffff window]
pci_bus 0000:00: root bus resource [io  0x0000-0xffff window]
pci_bus 0000:00: root bus resource [mem 0x8000000000-0xffffffffff window]
pci_bus 0000:00: root bus resource [bus 00-0f]
pci 0000:00:01.0: PCI bridge to [bus 01]
pci 0000:00:02.0: PCI bridge to [bus 02]
pci 0000:00:03.0: PCI bridge to [bus 03]
pci 0000:00:03.0: can't claim BAR 13 [io  0x0000-0x0fff]: no compatible bridge window
pci 0000:03:01.0: can't claim BAR 0 [io  0x0000-0x001f]: no compatible bridge window

While this never happens on x86, it is perfectly legal in general for a
PCI MMIO or IO window to start at address 0x0, and it was supported in
the code before commit f44116ae8818.

So let's drop the test for res->start != 0; resource_contains() already
checks whether [start, end) completely covers the resource, and so it
should be redundant.

Fixes: f44116ae8818 ("PCI: Remove pci_find_parent_resource() use for allocation")
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 drivers/pci/pci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 7904d02ffdb9..53a41b1f7ef7 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -454,7 +454,7 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev,
 	pci_bus_for_each_resource(bus, r, i) {
 		if (!r)
 			continue;
-		if (res->start && resource_contains(r, res)) {
+		if (resource_contains(r, res)) {
 
 			/*
 			 * If the window is prefetchable but the BAR is
-- 
2.9.3

^ permalink raw reply related

* [PATCH 0/2] arm64: acpi/pci: allow the firmware BAR configuration to be preserved
From: Ard Biesheuvel @ 2017-04-11 16:33 UTC (permalink / raw)
  To: linux-arm-kernel

This is a followup to the discussion regarding whether ACPI/arm64 systems
should preserve the PCI configuration performed by the firmware, or always
reconfigure it from scratch.

This series proposes to put it under the control of the firmware, by invoking
the _DSM method, and preserving the firmware configuration only when it
returns 0.

Patch #1 is a preparatory bugfix that solves the issue that I/O bridge
windows starting at 0x0 are dismissed when looking for parent resources.

Patch #2 adds the logic to invoke the _DSM and claim the existing
configuration rather than reallocate it from scratch if it returns '0'

Ard Biesheuvel (2):
  drivers: pci: do not disregard parent resources starting at 0x0
  arm64: acpi/pci: invoke _DSM whether to preserve firmware PCI setup

 arch/arm64/kernel/pci.c  | 20 ++++++++++++++++++--
 drivers/pci/pci.c        |  2 +-
 include/linux/pci-acpi.h |  7 ++++---
 3 files changed, 23 insertions(+), 6 deletions(-)

-- 
2.9.3

^ permalink raw reply

* [RFC PATCH 0/7] Cavium CN99xx SMMUv3 Errata workarounds
From: Will Deacon @ 2017-04-11 16:30 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1491921765-29475-1-git-send-email-linucherian@gmail.com>

On Tue, Apr 11, 2017 at 08:12:38PM +0530, linucherian at gmail.com wrote:
> From: Linu Cherian <linu.cherian@cavium.com>
> 
> Cavium CN99xx SMMUv3 implementation has two Silicon Erratas.
> 1. Errata ID #74
>    SMMU register alias Page 1 is not implemented
> 2. Errata ID #126
>    SMMU doesnt support unique IRQ lines for gerror, eventq and cmdq-sync

Is this device in production, or just part of a test chip?

Will

^ permalink raw reply

* [GIT PULL] pinctrl: samsung: Stuff for v4.12
From: Lee Jones @ 2017-04-11 16:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAJKOXPc7D_1xaJDG+W4JJ5YxRVJ868RU-Vmbh_r4J0g0Qjufdw@mail.gmail.com>

On Tue, 11 Apr 2017, Krzysztof Kozlowski wrote:

> On Tue, Apr 11, 2017 at 4:08 PM, Lee Jones <lee.jones@linaro.org> wrote:
> > On Sat, 08 Apr 2017, Krzysztof Kozlowski wrote:
> >
> >> Hi,
> >>
> >> Update for v4.12. Marek's commits were also shared (as separate sub-tag) to MFD
> >> (Lee Jones) for his work (it is a dependency).
> >
> > I already pulled Marek's patches into MFD.
> >
> > What are Charles' patches for?
> 
> This is a pull request for Linus Walleij, not for you. As you said,
> you already have all necessary dependencies for MFD.

Okay, so no need to sent it to me then, right? =;-)

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply

* [linux-sunxi] Re: [PATCH v2 03/11] dt-bindings: add device tree binding for X-Powers AXP803 PMIC
From: Lee Jones @ 2017-04-11 16:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAGb2v65UiFqjYES3hF-S6THZ5Xu-HRS9R7Ngk8xeP1rZajsr7w@mail.gmail.com>

On Tue, 11 Apr 2017, Chen-Yu Tsai wrote:

> Hi,
> 
> On Tue, Apr 11, 2017 at 11:00 PM, Icenowy Zheng <icenowy@aosc.io> wrote:
> >
> >
> > On Tue, 11 Apr 2017, Lee Jones wrote:
> >
> >> On Sat, 08 Apr 2017, Icenowy Zheng wrote:
> >>
> >>> AXP803 is a PMIC produced by Shenzhen X-Powers, with either I2C or RSB
> >>> bus.
> >>>
> >>> Add a compatible for it.
> >>>
> >>> Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
> >>> Acked-by: Chen-Yu Tsai <wens@csie.org>
> >>> ---
> >>> Changes in v2:
> >>> - Place AXP803 before AXP806/809.
> >>> - Added Chen-Yu's ACK.
> >>>
> >>>  Documentation/devicetree/bindings/mfd/axp20x.txt | 5 +++--
> >>>  1 file changed, 3 insertions(+), 2 deletions(-)
> >>
> >>
> >> For my own reference:
> >>  Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
> >
> >
> > Excuse me... Who will apply this patch?

Excuse you?  Are you trying to be polite, or rude?

I'm guessing due to the lagging '...', that it's the latter?

> > I think this patch should be applied by you as it's in bindings/mfd
> > directory, however, if I'm wrong, please point out ;-)
>
> We need the DT maintainers to ack it first. :)

We don't really need the DT Maintainers to get involved here.

What I do need is either clear direction as to how this
patch-set should be applied, or an indication of which patches are
dependant on others and which are orthogonal and can be applied
independently.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply

* [RFC PATCH v0.2] PCI: Add support for tango PCIe host bridge
From: Mason @ 2017-04-11 16:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5f81730d-fbe3-1f4c-de34-09bbfb893ee1@arm.com>

On 11/04/2017 17:49, Marc Zyngier wrote:
> On 11/04/17 16:13, Mason wrote:
>> On 27/03/2017 19:09, Marc Zyngier wrote:
>>
>>> Here's what your system looks like:
>>>
>>> PCI-EP -------> MSI Controller ------> INTC
>>>          MSI                    IRQ
>>>
>>> A PCI MSI is always edge. No ifs, no buts. That's what it is, and nothing
>>> else. Now, your MSI controller signals its output using a level interrupt,
>>> since you need to whack it on the head so that it lowers its line.
>>>
>>> There is not a single trigger, because there is not a single interrupt.
>>
>> Hello Marc,
>>
>> I was hoping you or Thomas might help clear some confusion
>> in my mind around IRQ domains (struct irq_domain).
>>
>> I have read https://www.kernel.org/doc/Documentation/IRQ-domain.txt
>>
>> IIUC, there should be one IRQ domain per IRQ controller.
>>
>> I have this MSI controller handling 256 interrupts, so I should
>> have *one* domain for all possible MSIs. Yet the Altera driver
>> registers *two* domains (msi_domain and inner_domain).
>>
>> Could I make everything work with a single IRQ domain?
> 
> No, because you have two irqchips. One that deals with the HW, and the
> other that deals with the MSIs how they are presented to the kernel,
> depending on the bus (PCI or something else). The fact that it doesn't
> really drive any HW doesn't make it irrelevant.

The example given in IRQ-domain.txt is

  Device --> IOAPIC -> Interrupt remapping Controller -> Local APIC -> CPU

with an irq_domain for each interrupt controller.


On my system I have:

  PCI-EP -> MSI controller -> System INTC -> GIC -> CPU

The driver for System INTC is drivers/irqchip/irq-tango.c
I think it has only one domain.

For the GIC, drivers/irqchip/irq-gic.c
I see a call to irq_domain_create_linear()

Is the handling of MSI different, and that is why we need
two domains? (Sorry, I did not understand that part well.)

When I looked at drivers/pci/host/pci-hyperv.c
they seem to have a single pci_msi_create_irq_domain call,
no call to domain_add or domain_create.
And they have a single struct irq_chip.

> You don't need to tell it anything about the number of interrupts you
> manage. As for your private structure, you've already given it to your
> low level domain, and there is no need to propagate it any further.

My main issue is that in the ack callback, I was in the "wrong"
domain, in that d->hwirq was not the MSI number. So I thought
I needed a single irq_domain.

Is there a function to map virq to the hwirq in any domain?

Regards.

^ permalink raw reply

* [PATCHv2 0/9] clk: ti: add support for clkctrl clocks
From: Tony Lindgren @ 2017-04-11 16:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <74c65472-cc1c-1ab9-7d27-65a164a6f025@ti.com>

* Tero Kristo <t-kristo@ti.com> [170410 11:36]:
> On 10/04/17 19:18, Tony Lindgren wrote:
> > * Tero Kristo <t-kristo@ti.com> [170410 00:34]:
> > > On 07/04/17 19:47, Tony Lindgren wrote:
> > > > OK hdmi works now on panda for both omapfb and omapdrm. Any ideas why
> > > > that change is now needed though?
> > > 
> > > Yes, it is pretty clear actually. Previously, the hwmod main clock was
> > > controlling the hdmi clock, but this is now rerouted to the clkctrl module
> > > clock, which is shared between all DSS submodules. This leaves the hdmi
> > > clock disabled, causing the failure.
> > > 
> > > The fix just puts the hdmi clock into the optional clocks list, and forces
> > > all the optional clocks on when pm_runtime is enabled for the module.
> > 
> > OK so no need to fix it in the current kernels then.
> 
> Yeah, just need to figure out a clean order of applying these patches. The
> hwmod data change for DSS modules can most likely be applied before the dts
> change for clkctrl is going in, as it is just causing double clk_enable /
> disable for the main clocks in that case.

Yeah no idea about the hwmod change needed for DSS.. Maybe patch it in
only in the no "ti,hwmods" path?

For the rest, how about the following for getting things patched with
a minimal patching needed to revert in case of regressions:

1. Let's add the clkctrl driver and it's dts clocks. Let's tag the
   new clkctrl clocks with status = "disabled" and not add the
   consumers into any existing devices

2. Let's modify the dts files to add the hwmod related items for the
   wrapper IP module but let's keep things probing the old way using
   compatible = "simple-bus" for now

3. Let's add the wrapper IP driver and make sure it does nothing
   except probe the children just like "simple-bus" currently does if
   the child module has the "ti,hwmods" property set

4. Then we can finally start flipping things over one module at a
   time just by removing "ti,hwmods" for the child device and
   status = "disabled" for the related module clock

Regards,

Tony

^ permalink raw reply

* [RFC PATCH 3/7] iommu/arm-smmu-v3: Introduce smmu option USE_SHARED_IRQS for Silicon errata
From: Will Deacon @ 2017-04-11 16:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <b84e2383-1a65-3c5a-8e2c-28eafdb20adc@arm.com>

On Tue, Apr 11, 2017 at 04:54:26PM +0100, Robin Murphy wrote:
> On 11/04/17 15:42, linucherian at gmail.com wrote:
> > From: Geetha <gakula@cavium.com>
> > 
> > Cavium 99xx SMMU implementation doesn't not support unique irq lines for
> > gerror, eventq and cmdq-sync. USE_SHARED_IRQS option enables to use single
> > irq line for all three interrupts.
> 
> AFAICS, there's nothing actually wrong with using shared wired IRQs -
> the architecture spec doesn't appear to say anything about it. I think
> it might suffice to simply add IRQF_SHARED if we can see the SMMU
> doesn't support MSIs anyway - it doesn't really seem like something we
> need to treat as a specific quirk.

No, this is not permitted by the spec. See 3.18.2 ("Interrupt sources"),
where it's clear that each source asserts a *unique* wired interrupt.

Geetha: does your implementation support MSIs?

Will

^ permalink raw reply

* [PATCH 0/7] arm64: marvell: add cryptographic engine support for 7k/8k
From: Gregory CLEMENT @ 2017-04-11 16:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170410093903.GA8035@gondor.apana.org.au>

Hi Herbert,
 
 On lun., avril 10 2017, Herbert Xu <herbert@gondor.apana.org.au> wrote:

> On Wed, Mar 29, 2017 at 02:44:25PM +0200, Antoine Tenart wrote:
>> Hi all,
>> 
>> This series adds support for the Inside Secure SafeXcel EIP197
>> cryptographic engine which can be found on Marvell Armada 7k and 8k
>> boards. A new cryptographic engine driver is added, as well as the
>> relevant device tree definition for the Armada 7040 DB and 8040 DB
>> boards.
>> 
>> This driver needs two firmwares to work correctly. These firmware are
>> usually used for more advanced operations than the ones supported (as of
>> now), but we still need them to pass the data to the internal
>> cryptographic engine.
>> 
>> This series was tested in various ways on both the Armada 7040 DB and
>> the Armada 8040 DB: using the crypto framework self tests, using tcrypt
>> and while performing various transfers with iperf on top of IPsec.
>> 
>> This series is based on top of v4.11-rc1, and is available on a branch
>> (which also contains the PPv2 support for 7k/8k, to ease the process of
>> testing IPsec): https://github.com/atenart/linux  v4.11-rc1/7k8k-crypto
>> I can rebase if needed.
>> 
>> Thanks,
>> Antoine
>> 
>> 
>> Antoine Tenart (7):
>>   Documentation/bindings: Document the SafeXel cryptographic engine
>>     driver
>>   crypto: inside-secure: add SafeXcel EIP197 crypto engine driver
>>   MAINTAINERS: add a maintainer for the Inside Secure crypto driver
>>   arm64: marvell: dts: add crypto engine description for 7k/8k
>>   arm64: marvell: dts: enable the crypto engine on the Armada 7040 DB
>>   arm64: marvell: dts: enable the crypto engine on the Armada 8040 DB
>>   arm64: defconfig: enable the Safexcel crypto engine as a module
>
> I have no problems with the crypto bits.

Does it means that you agree that I apply the arm-soc related patches
(from 4 to 7) to my mvebu trees?

Thanks,

Gregory

>
> Thanks,
> -- 
> Email: Herbert Xu <herbert@gondor.apana.org.au>
> Home Page: http://gondor.apana.org.au/~herbert/
> PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

^ permalink raw reply

* [PATCH v4 1/7] pinctrl: dt-bindings: Add documentation for Armada 37xx pin controllers
From: Gregory CLEMENT @ 2017-04-11 16:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170410181514.kzbpkaxkwf7uycdc@rob-hp-laptop>

Hi Linus,
 
 On lun., avril 10 2017, Rob Herring <robh@kernel.org> wrote:

> On Wed, Apr 05, 2017 at 05:18:02PM +0200, Gregory CLEMENT wrote:

>> +Example:
>> +pinctrl_sb: pinctrl-sb at 18800 {
>
> Just pinctrl at ...
>
>> +	compatible = "marvell,armada3710-sb-pinctrl", "syscon", "simple-mfd";
>> +	reg = <0x18800 0x100>, <0x18C00 0x20>;
>> +	gpiosb: gpiosb {
>
> gpio {
>
> With that,
>
> Acked-by: Rob Herring <robh@kernel.org>

Could you do this change while applying the patches, or do you want a
new series?

Also, if the binding is also OK for you, do you agree that I apply the
last 2 patches on the mvebu trees? (of course with the same changes
asked by Rob)

Thanks,

Gregory



>
>
>> +		#gpio-cells = <2>;
>> +		gpio-ranges = <&pinctrl_sb 0 0 29>;
>> +		gpio-controller;
>> +		interrupts =
>> +		<GIC_SPI 160 IRQ_TYPE_LEVEL_HIGH>,
>> +		<GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>,
>> +		<GIC_SPI 158 IRQ_TYPE_LEVEL_HIGH>,
>> +		<GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>,
>> +		<GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>;
>> +	};
>> +
>> +	rgmii_pins: mii-pins {
>> +		groups = "rgmii";
>> +		function = "mii";
>> +	};
>> +
>> +};
>> -- 
>> git-series 0.9.1

-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

^ permalink raw reply

* [PATCH v3] efifb: avoid reconfiguration of BAR that covers the framebuffer
From: Ard Biesheuvel @ 2017-04-11 16:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170411131639.GA6652@red-moon>

On 11 April 2017 at 14:16, Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> wrote:
> On Mon, Apr 10, 2017 at 06:29:27PM +0100, Ard Biesheuvel wrote:
>
> [...]
>
>> >>> So before starting the next round of hacking to work around this, I
>> >>> would like rekindle the discussion regarding the way we blindly
>> >>> reassign all resources on ACPI/arm64 systems, and whether there is a
>> >>> way imaginable to avoid doing that.
>> >>
>> >> There is a way if the whole ARM ecosystem work together to sort this
>> >> out and we think that's the right way to do; I am personally not
>> >> entirely convinced about that.
>> >>
>> >
>> > So what are the pros and cons here? EFI fb is not a hugely important
>> > use case, but it is one that relies on BARs staying where they are.
>> > Are there others like that?
>
> Not that I am aware of, which means that pros are thin on the ground.
>

OK. Sinan?

>> >>> I suppose the state of the BARs as we inherit it from the firmware
>> >>> cannot be blindly trusted (and IIUC, this is Lorenzo's primary issue
>> >>> with it). So should there be some side channel (UEFI config table
>> >>> perhaps?) to describe this?
>> >>
>> >> PCI firmware specifications rev 3.1, 4.6.5, "_DSM for Ignoring PCI
>> >> Boot Configurations".
>> >>
>> >> Do we want to enforce it on ARM ? I do not know to be honest (and it
>> >> still would not solve the DT firmware case).
>> >>
>> >
>> > No, it doesn't. But that doesn't mean we shouldn't solve it on ACPI if
>> > the pros outweigh the cons.
>
> No one is screaming for that to happen, I can implement resource
> claiming but at the moment I do not see the benefit.
>
> [...]
>
>> > The reason is to eliminate another unknown from the discussion whether
>> > UEFI can be expected to leave the entire PCI hierarchy in a sane
>> > state.
>> >
>> >> If we want to try to claim the whole resource tree on boot (in ACPI)
>> >> I can send a patch for that but there will be regressions.
>> >>
>> >
>> > I would like to see it, yes.
>>
>> FWIW, the following minimal [naive] patch
>>
>> diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
>> index 4f0e3ebfea4b..37c4d2f116a4 100644
>> --- a/arch/arm64/kernel/pci.c
>> +++ b/arch/arm64/kernel/pci.c
>> @@ -27,7 +27,7 @@
>>   */
>>  void pcibios_fixup_bus(struct pci_bus *bus)
>>  {
>> -       /* nothing to do, expected to be removed in the future */
>> +       pci_read_bridge_bases(bus);
>>  }
>>
>>  /*
>> @@ -208,8 +208,8 @@ struct pci_bus *pci_acpi_scan_root(struct
>> acpi_pci_root *root)
>>         if (!bus)
>>                 return NULL;
>>
>> -       pci_bus_size_bridges(bus);
>> -       pci_bus_assign_resources(bus);
>> +       pci_assign_unassigned_root_bus_resources(bus);
>> +       pci_bus_claim_resources(bus);
>
> I do not understand this code. If you reassign the whole thing
> before claiming it I am not sure what's the point of claiming
> the resources later, that's basically a nop. pci_bus_claim_resources()
> should suffice (if FW set-up is sane - which also reads bridge bases,
> BTW).
>

OK, I'm struggling a bit with this code. As you know, it is not light
bedtime reading :-)

In any case, I am starting to see your point. While I am able to claim
most of the configuration by calling pci_bus_claim_resources() only
[modulo some I/O BARs for which I will send out a bugfix shortly], the
option ROM BARs are left disabled by UEFI, and so I end up with

pci 0000:00:01.0: PCI bridge to [bus 01]
pci 0000:00:02.0: PCI bridge to [bus 02]
pci 0000:00:03.0: PCI bridge to [bus 03]
pci 0000:02:01.0: can't claim BAR 6 [mem 0xffff0000-0xffffffff pref]:
no compatible bridge window
pci 0000:03:01.0: can't claim BAR 6 [mem 0xfffc0000-0xffffffff pref]:
no compatible bridge window

and everything else works fine.

Both PPC and x86 have similar code to infer from the BARs themselves
whether they were programmed, but in our case, it comes down to
checking whether a parent resource exists that covers the range.

It may still make sense to add support for the _DSM method, and leave
it to the firmware to tell the kernel whether the PCI configuration is
supposed to be correct, so I will send out an RFC for that as well.

^ permalink raw reply

* [PATCH 2/2] ARM: imx_v6_v7_defconfig: enable PCI_MSI
From: Tim Harvey @ 2017-04-11 16:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1491394571.2904.44.camel@pengutronix.de>

On Wed, Apr 5, 2017 at 5:16 AM, Lucas Stach <l.stach@pengutronix.de> wrote:
> Am Mittwoch, den 05.04.2017, 13:07 +0100 schrieb Joao Pinto:
> [...]
>> >>> Lucas,
>> >>>
>> >>> I feel a bit dumb here for forgetting about this issue and letting it
>> >>> drop, but I've just spent a day debugging an issue with the ath9k
>> >>> wireless driver only to realize it was that the ath9k card and/or
>> >>> driver doesn't support MSI interrupts and thus no longer work on
>> >>> mainline kernels that have MSI enabled. To be honest I'm not sure how
>> >>> many PCIe devices out there can't support MSI because of hardware
>> >>> limitations vs drivers that simply have not implemented it.
>> >>>
>> >>> To make matters worse 3ee803641e76bea76ec730c80dcc64739a9919ff makes
>> >>> it such that you can't disable MSI on the IMX6.
>> >>>
>> >>> Is there some fix you can think of to make the IMX6 PCIe host
>> >>> controller raise an interrupt for cards/drivers that use legacy irq's?
>> >>> If not, then we need to allow MSI to be disabled for IMX6 and default
>> >>> it to disabled for compatibility.
>> >>>
>> >>
>> >> Joao / Jingoo,
>> >>
>> >> (Adding linux-pci to the list to pick up maintainers of other DWC PCIe
>> >> core drivers)
>> >>
>> >> Is Luca's statement that 'legacy IRQs and MSIs can't be used together'
>> >> true in general for the DW PCIe core? If any of the host controllers
>> >> using this core can't support both legacy and MSI irqs together I
>> >> still believe we shouldn't enable/require MSI as it breaks any
>> >> card/driver that only supports legacy interrupts (such as ath9k).
>> >
>> > If there are 2 DW PCIe controllers, one controller can be used for legacy
>> > and another controller can be used for MSI.
>> >
>> > But, I am not sure that one DW PCIe controller can support both MSI device
>> > and legacy interrupt device at the same time.
>> >
>> > To Joao Pinto,
>> > Will you confirm this?
>> >
>>
>> Hi Jingoo,
>> I confirm that if a RC has MSI enable, it won't support legacy interrupts.
>
> Okay, so the only way to solve this in a generic way, that I can see at
> the moment is to defer MSI enable until the downstream device actually
> requests an MSI irq.
>
> Also we need to disable the MSI capability if any bridge device turns up
> during the bus topology scan, as we can have devices with conflicting
> requirements connected to the RC in that case.
>
> I'll cook up some patches to implement this.
>

Lucas,

Any thoughts yet? I'll likely want to backport this to stable although
the restructuring to drivers/pci/dwc and subsequent changes since 4.10
will likely require a re-written patch.

Best regards,

Tim

^ permalink raw reply

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
From: Rob Herring @ 2017-04-11 16:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170411143250.3r7ys3rjshtxgaee@sirena.org.uk>

On Tue, Apr 11, 2017 at 9:32 AM, Mark Brown <broonie@kernel.org> wrote:
> On Mon, Apr 10, 2017 at 02:48:32PM -0500, Rob Herring wrote:
>> On Thu, Apr 06, 2017 at 05:40:35PM +0200, olivier moysan wrote:
>
>> > +Required properties:
>> > +  - compatible: Must be "st,stm32h7-i2s"
>> > +  - #sound-dai-cells: Must be 1. (one parameter)
>> > +    This parameter allows to specify CPU DAI index in soundcard CPU dai link.
>> > +   index 0: playback DAI
>> > +   index 1: capture DAI
>> > +   index 2: full duplex DAI
>
>> Is this still needed for graph-card?
>
> The graph card is blocked on your review...

Maybe if there were more reviewers it would move faster. I don't know
ASoC that well.

> I'm also not clear how
> without something like this we'd be able to identify a specific DAI
> within a device if we don't have a way of identifying them.

Isn't that what the graph does? "dais" points to a list of ports which
are the specific DAIs whether there are multiple ones in a single
device or multiple devices with a single DAI each.

Rob

^ permalink raw reply

* [PATCH 2/3] power: supply: New driver for LEGO MINDSTORMS EV3 battery
From: Sebastian Reichel @ 2017-04-11 16:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1491251029-23256-3-git-send-email-david@lechnology.com>

Hi,

Driver looks fine. One minor thing, though:

On Mon, Apr 03, 2017 at 03:23:48PM -0500, David Lechner wrote:
> [...]
>
> +	batt->psy = power_supply_register(dev, &lego_ev3_battery_desc, &psy_cfg);
> +	err = PTR_ERR_OR_ZERO(batt->psy);
> +	if (err) {
> +		dev_err(dev, "failed to register power supply\n");
> +		return err;
> +	}

There is devm_power_supply_register() and it makes sense to use it,
since that means you can drop the remove function completly :)

> +	return 0;
> +}
> +
> +static int lego_ev3_battery_remove(struct platform_device *pdev)
> +{
> +	struct lego_ev3_battery *batt = platform_get_drvdata(pdev);
> +
> +	power_supply_unregister(batt->psy);
> +
> +	return 0;
> +}

-- Sebastian
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170411/5eecb7ba/attachment.sig>

^ permalink raw reply

* [PATCH v4 0/2] Handle Cavium ThunderX2 PCI topology quirk
From: David Daney @ 2017-04-11 16:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170411134407.GB31773@bhelgaas-glaptop.roam.corp.google.com>

On 04/11/2017 06:44 AM, Bjorn Helgaas wrote:
> [+cc David]
>
> I forgot to mention that I'm also hoping for an ack from David, since
> he's listed as the maintainer of the ThunderX drivers.
>

JC is really leading the development of this particular PCI 
implementation, but I am happy to supply my:

Acked-by: David Daney <david.daney@cavium.com>



> On Mon, Apr 03, 2017 at 01:15:02PM +0000, Jayachandran C wrote:
>> Hi Bjorn, Alex,
>>
>> Sending this again (with a trivial fix to author name), please review.
>> Updated summary below:
>>
>> Here is v4 of the patchset to handle the PCIe topology quirk of Cavium
>> ThunderX2 systems (previously known as Broadcom Vulcan).
>>
>> The earlier discussions on this can be seen at:
>> http://www.spinics.net/lists/linux-pci/msg51001.html
>> https://patchwork.ozlabs.org/patch/582633/ and
>> https://lists.linuxfoundation.org/pipermail/iommu/2016-June/017681.html
>>
>> The earlier discussion on this patchset ended with a suggestion that it
>> may be possible to fix up this quirk by handling the issue in the
>> function argument of pci_for_each_dma_alias(). But at that point we did
>> not have the codebase to make the changes since the full ACPI and OF code
>> for SMMU and GIC ITS was not upstream.
>>
>> Now that the changes are upstream, I tried to fix it in both the SMMU
>> and the GIC ITS code based on this suggestion, the changes needed are at:
>>  https://github.com/jchandra-cavm/linux/commits/rid-xlate-fixup
>>
>> The problems with this approach are:
>>  - of the 14 uses of pci_for_each_dma_alias in the function in the kernel
>>    tree, I have to fixup 6 callers (which is all but one ofthe callers
>>    outside x86)
>>  - 4 of these can be reasonably handled (please see the github repo above),
>>    but the calls in drivers/irqchip/irq-gic-v3-its-pci-msi.c and
>>    drivers/iommu/iommu.c cannot be reasonably fixed up.
>>  - Even without the 2 above two changes I can get it to work for now.
>>    But pci_for_each_dma_alias does not work as expected on this platform
>>    and we have to be aware of that for all future uses of the function.
>>
>> For now, I have ruled out the approach, and I have rebased the earlier
>> patch on to 4.11-rc and submitting again for review. The changes are:
>>
>> v3->v4:
>>  - new address of author
>>
>> v2>v3:
>>  - changed device flag name from PCI_DEV_FLAGS_DMA_ALIAS_ROOT to
>>    PCI_DEV_FLAGS_BRIDGE_XLATE_ROOT
>>  - updated commit message to make the quirk clearer.
>>
>> Let me know your comments and suggestions.
>>
>> Thanks,
>> JC.
>>
>>
>> Jayachandran C (2):
>>   PCI: Add device flag PCI_DEV_FLAGS_BRIDGE_XLATE_ROOT
>>   PCI: quirks: Fix ThunderX2 dma alias handling
>>
>>  drivers/pci/quirks.c | 14 ++++++++++++++
>>  drivers/pci/search.c |  4 ++++
>>  include/linux/pci.h  |  2 ++
>>  3 files changed, 20 insertions(+)
>>
>> --
>> 2.7.4
>>
>>
>> _______________________________________________
>> 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

* [RFC PATCH 4/7] ACPICA: IORT: Add SMMuV3 model definitions.
From: Robin Murphy @ 2017-04-11 15:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1491921765-29475-5-git-send-email-linucherian@gmail.com>

On 11/04/17 15:42, linucherian at gmail.com wrote:
> From: Linu Cherian <linu.cherian@cavium.com>
> 
> Add SMMuV3 model definitions.
> 
> Signed-off-by: Linu Cherian <linu.cherian@cavium.com>
> ---
>  include/acpi/actbl2.h | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h
> index 2b4af07..9db67d6 100644
> --- a/include/acpi/actbl2.h
> +++ b/include/acpi/actbl2.h
> @@ -778,6 +778,11 @@ struct acpi_iort_smmu {
>  #define ACPI_IORT_SMMU_CORELINK_MMU400  0x00000002	/* ARM Corelink MMU-400 */
>  #define ACPI_IORT_SMMU_CORELINK_MMU500  0x00000003	/* ARM Corelink MMU-500 */
>  
> +#define ACPI_IORT_SMMU_V3               0x00000000      /* Generic SMMUv3 */
> +#define ACPI_IORT_SMMU_CORELINK_MMU600  0x00000001      /* ARM Corelink MMU-600 */
> +#define ACPI_IORT_SMMU_V3_HISILICON     0x00000002      /* HiSilicon SMMUv3 */
> +#define ACPI_IORT_SMMU_V3_CAVIUM_CN99XX 0x00000003      /* Cavium CN99xx SMMUv3 */

None of those models are listed in the current IORT spec.

What's MMU-600?

Robin.

> +
>  /* Masks for Flags field above */
>  
>  #define ACPI_IORT_SMMU_DVM_SUPPORTED    (1)
> 

^ permalink raw reply

* [PATCH 3/5] Memory hotplug support for arm64 platform (v2)
From: Mark Rutland @ 2017-04-11 15:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50119579bdbb23a4d888d13b0a46cb2e027839ed.1491920513.git.ar@linux.vnet.ibm.com>

Hi,

On Tue, Apr 11, 2017 at 03:55:22PM +0100, Andrea Reale wrote:
> From: Maciej Bielski <m.bielski@virtualopensystems.com>
> 
> This is a second and improved version of the patch previously released
> in [3].
> 
> It builds on the work by Scott Branden [2] and, henceforth,
> it needs to be applied on top of Scott's patches [2].
> Comments are very welcome.
> 
> Changes from the original patchset and known issues:
> 
> - Compared to Scott's original patchset, this work adds the mapping of
>   the new hotplugged pages into the kernel page tables. This is done by
>   copying the old swapper_pg_dir over a new page, adding the new mappings,
>   and then switching to the newly built pg_dir (see `hotplug_paging` in
>   arch/arm64/mmu.c). There might be better ways to to this: suggestions
>   are more than welcome.

For this reply, I'm just going to focus on the PGD replacement.

It's not clear to me if we need to swap the PGD, since everything we do
here is strictly additive and non-conflicting, and it should be safe to
add things to the swapper_pg_dir live.

However, assuming there's something I've missed, I have some comments
below.

[...]

> +#ifdef CONFIG_MEMORY_HOTPLUG
> +
> +/*
> + * hotplug_paging() is used by memory hotplug to build new page tables
> + * for hot added memory.
> + */
> +void hotplug_paging(phys_addr_t start, phys_addr_t size)
> +{
> +
> +	struct page *pg;
> +	phys_addr_t pgd_phys = pgd_pgtable_alloc();
> +	pgd_t *pgd = pgd_set_fixmap(pgd_phys);
> +
> +	memcpy(pgd, swapper_pg_dir, PAGE_SIZE);

s/PAGE_SIZE/PGD_SIZE/ (and below, too).

See commit 12f043ff2b28fa64 ("arm64: fix warning about swapper_pg_dir
overflow").

> +
> +	__create_pgd_mapping(pgd, start, __phys_to_virt(start), size,
> +		PAGE_KERNEL, pgd_pgtable_alloc, false);
> +
> +	cpu_replace_ttbr1(__va(pgd_phys));

Other CPUs may be online at this point, and cpu_replace_ttbr1() was only
intended for UP operation. Other CPUs will still have swapper_pg_dir in
their TTBR1_EL1 at this point in time...

> +	memcpy(swapper_pg_dir, pgd, PAGE_SIZE);

... which this will alter, in an unsafe fashion.

The other CPUs are live, and might be altering swapper. e.g. one might
be using the fixmap to alter code. We will need some stop_machine()-like
machinery here to synchronise with other CPUs, ensuring that they don't
have swapper_pg_dir live.

Unfortunately, we can't change other to the temporary pgdir in a cross
call, then fix things up, then do another cross call. If any exception
is taken when we're in the temporary pgdir, __uaccess_ttbr0_disable will
install junk into TTBR0, and we risk the usual set of pain junk TLBs
entail.

We appear to have a latent bug at boot time along those lines, when
setting up the page tables and initialising KASAN. I'll see about
cleaning that up.

Thanks,
Mark.

^ permalink raw reply

* [RFC PATCH 3/7] iommu/arm-smmu-v3: Introduce smmu option USE_SHARED_IRQS for Silicon errata
From: Robin Murphy @ 2017-04-11 15:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1491921765-29475-4-git-send-email-linucherian@gmail.com>

On 11/04/17 15:42, linucherian at gmail.com wrote:
> From: Geetha <gakula@cavium.com>
> 
> Cavium 99xx SMMU implementation doesn't not support unique irq lines for
> gerror, eventq and cmdq-sync. USE_SHARED_IRQS option enables to use single
> irq line for all three interrupts.

AFAICS, there's nothing actually wrong with using shared wired IRQs -
the architecture spec doesn't appear to say anything about it. I think
it might suffice to simply add IRQF_SHARED if we can see the SMMU
doesn't support MSIs anyway - it doesn't really seem like something we
need to treat as a specific quirk.

> Signed-off-by: Geetha Sowjanya <gakula@cavium.com>
> ---
>  drivers/iommu/arm-smmu-v3.c | 18 +++++++++++++++---
>  1 file changed, 15 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
> index b326195..1475ad8 100644
> --- a/drivers/iommu/arm-smmu-v3.c
> +++ b/drivers/iommu/arm-smmu-v3.c
> @@ -415,6 +415,9 @@
>  #define ARM_SMMU_PAGE0_REGS_ONLY(s)		\
>  	((s)->options & ARM_SMMU_OPT_PAGE0_REGS_ONLY)
>  
> +#define ARM_SMMU_USE_SHARED_IRQS(s)		\
> +	((s)->options & ARM_SMMU_OPT_USE_SHARED_IRQS)
> +
>  static bool disable_bypass;
>  module_param_named(disable_bypass, disable_bypass, bool, S_IRUGO);
>  MODULE_PARM_DESC(disable_bypass,
> @@ -601,6 +604,7 @@ struct arm_smmu_device {
>  
>  #define ARM_SMMU_OPT_SKIP_PREFETCH	(1 << 0)
>  #define ARM_SMMU_OPT_PAGE0_REGS_ONLY    (1 << 1)
> +#define ARM_SMMU_OPT_USE_SHARED_IRQS    (1 << 2)
>  	u32				options;
>  
>  	struct arm_smmu_cmdq		cmdq;
> @@ -668,6 +672,7 @@ struct arm_smmu_option_prop {
>  static struct arm_smmu_option_prop arm_smmu_options[] = {
>  	{ ARM_SMMU_OPT_SKIP_PREFETCH, "hisilicon,broken-prefetch-cmd" },
>  	{ ARM_SMMU_OPT_PAGE0_REGS_ONLY, "cavium-cn99xx,broken-page1-regspace"},
> +	{ ARM_SMMU_OPT_USE_SHARED_IRQS, "cavium-cn99xx,broken-unique-irqlines"},
>  	{ 0, NULL},
>  };
>  
> @@ -2237,6 +2242,7 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
>  {
>  	int ret, irq;
>  	u32 irqen_flags = IRQ_CTRL_EVTQ_IRQEN | IRQ_CTRL_GERROR_IRQEN;
> +	u32 irqflags = IRQF_ONESHOT | IRQF_SHARED;

Either way, this is a really ugly way to go about it - I'd much rather
initialise the common base value:

	u32 irqflags = (definitely not an MSI) ? IRQF_SHARED : 0;

>  
>  	/* Disable IRQs first */
>  	ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_IRQ_CTRL,
> @@ -2251,9 +2257,11 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
>  	/* Request interrupt lines */
>  	irq = smmu->evtq.q.irq;
>  	if (irq) {
> +		if (!ARM_SMMU_USE_SHARED_IRQS(smmu))
> +			irqflags = IRQF_ONESHOT;
>  		ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
>  						arm_smmu_evtq_thread,
> -						IRQF_ONESHOT,

...and just pass irqflags | IRQF_ONESHOT here (and irqflags elsewhere),
without all the horrible copy-paste conditions.

Robin.

> +						irqflags,
>  						"arm-smmu-v3-evtq", smmu);
>  		if (ret < 0)
>  			dev_warn(smmu->dev, "failed to enable evtq irq\n");
> @@ -2261,8 +2269,10 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
>  
>  	irq = smmu->cmdq.q.irq;
>  	if (irq) {
> +		if (!ARM_SMMU_USE_SHARED_IRQS(smmu))
> +			irqflags = 0;
>  		ret = devm_request_irq(smmu->dev, irq,
> -				       arm_smmu_cmdq_sync_handler, 0,
> +				       arm_smmu_cmdq_sync_handler, irqflags,
>  				       "arm-smmu-v3-cmdq-sync", smmu);
>  		if (ret < 0)
>  			dev_warn(smmu->dev, "failed to enable cmdq-sync irq\n");
> @@ -2270,8 +2280,10 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
>  
>  	irq = smmu->gerr_irq;
>  	if (irq) {
> +		if (!ARM_SMMU_USE_SHARED_IRQS(smmu))
> +			irqflags = 0;
>  		ret = devm_request_irq(smmu->dev, irq, arm_smmu_gerror_handler,
> -				       0, "arm-smmu-v3-gerror", smmu);
> +				       irqflags, "arm-smmu-v3-gerror", smmu);
>  		if (ret < 0)
>  			dev_warn(smmu->dev, "failed to enable gerror irq\n");
>  	}
> 

^ permalink raw reply

* [RFC PATCH v0.2] PCI: Add support for tango PCIe host bridge
From: Marc Zyngier @ 2017-04-11 15:49 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <30f662a6-5dab-515b-e35a-a312f3c7b509@free.fr>

On 11/04/17 16:13, Mason wrote:
> On 27/03/2017 19:09, Marc Zyngier wrote:
> 
>> Here's what your system looks like:
>>
>> PCI-EP -------> MSI Controller ------> INTC
>>          MSI                    IRQ
>>
>> A PCI MSI is always edge. No ifs, no buts. That's what it is, and nothing
>> else. Now, your MSI controller signals its output using a level interrupt,
>> since you need to whack it on the head so that it lowers its line.
>>
>> There is not a single trigger, because there is not a single interrupt.
> 
> Hello Marc,
> 
> I was hoping you or Thomas might help clear some confusion
> in my mind around IRQ domains (struct irq_domain).
> 
> I have read https://www.kernel.org/doc/Documentation/IRQ-domain.txt
> 
> IIUC, there should be one IRQ domain per IRQ controller.
> 
> I have this MSI controller handling 256 interrupts, so I should
> have *one* domain for all possible MSIs. Yet the Altera driver
> registers *two* domains (msi_domain and inner_domain).
> 
> Could I make everything work with a single IRQ domain?

No, because you have two irqchips. One that deals with the HW, and the
other that deals with the MSIs how they are presented to the kernel,
depending on the bus (PCI or something else). The fact that it doesn't
really drive any HW doesn't make it irrelevant.

> I'm confused by the sequence:
> 
>   irq_dom = irq_domain_create_linear(fwnode, MSI_COUNT, &msi_dom_ops, pcie);
>   msi_dom = pci_msi_create_irq_domain(fwnode, &msi_domain_info, irq_dom);
> 
> It seems I should be able to call only pci_msi_create_irq_domain...
> And the parent of the MSI controller is given in the DT.

No, see above.

> I'm not quite sure how I tell pci_msi_create_irq_domain how many
> MSIs it's supposed to manage. Nor how I pass my private struct.

You don't need to tell it anything about the number of interrupts you
manage. As for your private structure, you've already given it to your
low level domain, and there is no need to propagate it any further.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply

* [PATCH] clockevents/tcb_clksrc: implement suspend/resume
From: Alexandre Belloni @ 2017-04-11 15:48 UTC (permalink / raw)
  To: linux-arm-kernel

On sama5d2, power to the core may be cut while entering suspend mode. It is
necessary to save and restore the TCB registers.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 drivers/clocksource/tcb_clksrc.c | 46 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c
index d4ca9962a759..57f5d72328f4 100644
--- a/drivers/clocksource/tcb_clksrc.c
+++ b/drivers/clocksource/tcb_clksrc.c
@@ -9,6 +9,7 @@
 #include <linux/ioport.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
+#include <linux/syscore_ops.h>
 #include <linux/atmel_tc.h>
 
 
@@ -40,6 +41,14 @@
  */
 
 static void __iomem *tcaddr;
+static struct
+{
+	u32 cmr;
+	u32 imr;
+	u32 rc;
+	bool clken;
+} tcb_cache[3];
+static u32 bmr_cache;
 
 static u64 tc_get_cycles(struct clocksource *cs)
 {
@@ -61,12 +70,49 @@ static u64 tc_get_cycles32(struct clocksource *cs)
 	return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV));
 }
 
+void tc_clksrc_suspend(struct clocksource *cs)
+{
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		tcb_cache[i].cmr = readl(tcaddr + ATMEL_TC_REG(i, CMR));
+		tcb_cache[i].imr = readl(tcaddr + ATMEL_TC_REG(i, IMR));
+		tcb_cache[i].rc = readl(tcaddr + ATMEL_TC_REG(i, RC));
+		tcb_cache[i].clken = !!(readl(tcaddr + ATMEL_TC_REG(i, SR)) &
+					ATMEL_TC_CLKSTA);
+	}
+
+	bmr_cache = readl(tcaddr + ATMEL_TC_BMR);
+}
+
+void tc_clksrc_resume(struct clocksource *cs)
+{
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		__raw_writel(tcb_cache[i].cmr, tcaddr + ATMEL_TC_REG(i, CMR));
+		__raw_writel(0, tcaddr + ATMEL_TC_REG(i, RA));
+		__raw_writel(0, tcaddr + ATMEL_TC_REG(i, RB));
+		__raw_writel(tcb_cache[i].rc, tcaddr + ATMEL_TC_REG(i, RC));
+		__raw_writel(0xff, tcaddr + ATMEL_TC_REG(i, IDR));
+		__raw_writel(tcb_cache[i].imr, tcaddr + ATMEL_TC_REG(i, IER));
+		if (tcb_cache[i].clken)
+			__raw_writel(ATMEL_TC_CLKEN, tcaddr +
+				     ATMEL_TC_REG(i, CCR));
+	}
+
+	writel(bmr_cache, tcaddr + ATMEL_TC_BMR);
+	writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
+}
+
 static struct clocksource clksrc = {
 	.name           = "tcb_clksrc",
 	.rating         = 200,
 	.read           = tc_get_cycles,
 	.mask           = CLOCKSOURCE_MASK(32),
 	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
+	.suspend	= tc_clksrc_suspend,
+	.resume		= tc_clksrc_resume,
 };
 
 #ifdef CONFIG_GENERIC_CLOCKEVENTS
-- 
2.11.0

^ permalink raw reply related

* [PATCH v4 2/2] ASoC: stm32: add SAI driver
From: olivier moysan @ 2017-04-11 15:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1491925646-29113-1-git-send-email-olivier.moysan@st.com>

This patch implements SAI ASoC driver for STM32.

Signed-off-by: olivier moysan <olivier.moysan@st.com>
---
 sound/soc/Kconfig             |   1 +
 sound/soc/Makefile            |   1 +
 sound/soc/stm/Kconfig         |   8 +
 sound/soc/stm/Makefile        |   6 +
 sound/soc/stm/stm32_sai.c     | 115 ++++++
 sound/soc/stm/stm32_sai.h     | 199 ++++++++++
 sound/soc/stm/stm32_sai_sub.c | 881 ++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 1211 insertions(+)
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_sai.c
 create mode 100644 sound/soc/stm/stm32_sai.h
 create mode 100644 sound/soc/stm/stm32_sai_sub.c

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 9df9658..c0abad2 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -64,6 +64,7 @@ source "sound/soc/sh/Kconfig"
 source "sound/soc/sirf/Kconfig"
 source "sound/soc/spear/Kconfig"
 source "sound/soc/sti/Kconfig"
+source "sound/soc/stm/Kconfig"
 source "sound/soc/sunxi/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 2f6aabb..39c27a5 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_SND_SOC)	+= sh/
 obj-$(CONFIG_SND_SOC)	+= sirf/
 obj-$(CONFIG_SND_SOC)	+= spear/
 obj-$(CONFIG_SND_SOC)	+= sti/
+obj-$(CONFIG_SND_SOC)	+= stm/
 obj-$(CONFIG_SND_SOC)	+= sunxi/
 obj-$(CONFIG_SND_SOC)	+= tegra/
 obj-$(CONFIG_SND_SOC)	+= txx9/
diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig
new file mode 100644
index 0000000..a6372de
--- /dev/null
+++ b/sound/soc/stm/Kconfig
@@ -0,0 +1,8 @@
+menuconfig SND_SOC_STM32
+	tristate "STMicroelectronics STM32 SOC audio support"
+	depends on ARCH_STM32 || COMPILE_TEST
+	depends on SND_SOC
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	select REGMAP_MMIO
+	help
+	  Say Y if you want to enable ASoC support for STM32
diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
new file mode 100644
index 0000000..e466a47
--- /dev/null
+++ b/sound/soc/stm/Makefile
@@ -0,0 +1,6 @@
+# SAI
+snd-soc-stm32-sai-sub-objs := stm32_sai_sub.o
+obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai-sub.o
+
+snd-soc-stm32-sai-objs := stm32_sai.o
+obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai.o
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c
new file mode 100644
index 0000000..6159d66
--- /dev/null
+++ b/sound/soc/stm/stm32_sai.c
@@ -0,0 +1,115 @@
+/*
+ * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/reset.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/core.h>
+
+#include "stm32_sai.h"
+
+static const struct of_device_id stm32_sai_ids[] = {
+	{ .compatible = "st,stm32f4-sai", .data = (void *)SAI_STM32F4 },
+	{}
+};
+
+static int stm32_sai_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct stm32_sai_data *sai;
+	struct reset_control *rst;
+	struct resource *res;
+	void __iomem *base;
+	const struct of_device_id *of_id;
+
+	sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+	if (!sai)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	of_id = of_match_device(stm32_sai_ids, &pdev->dev);
+	if (of_id)
+		sai->version = (enum stm32_sai_version)of_id->data;
+	else
+		return -EINVAL;
+
+	sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k");
+	if (IS_ERR(sai->clk_x8k)) {
+		dev_err(&pdev->dev, "missing x8k parent clock\n");
+		return PTR_ERR(sai->clk_x8k);
+	}
+
+	sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k");
+	if (IS_ERR(sai->clk_x11k)) {
+		dev_err(&pdev->dev, "missing x11k parent clock\n");
+		return PTR_ERR(sai->clk_x11k);
+	}
+
+	/* init irqs */
+	sai->irq = platform_get_irq(pdev, 0);
+	if (sai->irq < 0) {
+		dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
+		return sai->irq;
+	}
+
+	/* reset */
+	rst = reset_control_get(&pdev->dev, NULL);
+	if (!IS_ERR(rst)) {
+		reset_control_assert(rst);
+		udelay(2);
+		reset_control_deassert(rst);
+	}
+
+	sai->pdev = pdev;
+	platform_set_drvdata(pdev, sai);
+
+	return of_platform_populate(np, NULL, NULL, &pdev->dev);
+}
+
+static int stm32_sai_remove(struct platform_device *pdev)
+{
+	of_platform_depopulate(&pdev->dev);
+
+	return 0;
+}
+
+MODULE_DEVICE_TABLE(of, stm32_sai_ids);
+
+static struct platform_driver stm32_sai_driver = {
+	.driver = {
+		.name = "st,stm32-sai",
+		.of_match_table = stm32_sai_ids,
+	},
+	.probe = stm32_sai_probe,
+	.remove = stm32_sai_remove,
+};
+
+module_platform_driver(stm32_sai_driver);
+
+MODULE_DESCRIPTION("STM32 Soc SAI Interface");
+MODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>");
+MODULE_ALIAS("platform:st,stm32-sai");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h
new file mode 100644
index 0000000..270be93
--- /dev/null
+++ b/sound/soc/stm/stm32_sai.h
@@ -0,0 +1,199 @@
+/*
+ * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * 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.
+ */
+
+/******************** SAI Register Map **************************************/
+
+/* common register */
+#define STM_SAI_GCR		0x00
+
+/* Sub-block A&B registers offsets, relative to A&B sub-block addresses */
+#define STM_SAI_CR1_REGX	0x00	/* A offset: 0x04. B offset: 0x24 */
+#define STM_SAI_CR2_REGX	0x04
+#define STM_SAI_FRCR_REGX	0x08
+#define STM_SAI_SLOTR_REGX	0x0C
+#define STM_SAI_IMR_REGX	0x10
+#define STM_SAI_SR_REGX		0x14
+#define STM_SAI_CLRFR_REGX	0x18
+#define STM_SAI_DR_REGX		0x1C
+
+/******************** Bit definition for SAI_GCR register *******************/
+#define SAI_GCR_SYNCIN_SHIFT	0
+#define SAI_GCR_SYNCIN_MASK	GENMASK(1, SAI_GCR_SYNCIN_SHIFT)
+#define SAI_GCR_SYNCIN_SET(x)	((x) << SAI_GCR_SYNCIN_SHIFT)
+
+#define SAI_GCR_SYNCOUT_SHIFT	4
+#define SAI_GCR_SYNCOUT_MASK	GENMASK(5, SAI_GCR_SYNCOUT_SHIFT)
+#define SAI_GCR_SYNCOUT_SET(x)	((x) << SAI_GCR_SYNCOUT_SHIFT)
+
+/******************* Bit definition for SAI_XCR1 register *******************/
+#define SAI_XCR1_RX_TX_SHIFT	0
+#define SAI_XCR1_RX_TX		BIT(SAI_XCR1_RX_TX_SHIFT)
+#define SAI_XCR1_SLAVE_SHIFT	1
+#define SAI_XCR1_SLAVE		BIT(SAI_XCR1_SLAVE_SHIFT)
+
+#define SAI_XCR1_PRTCFG_SHIFT	2
+#define SAI_XCR1_PRTCFG_MASK	GENMASK(3, SAI_XCR1_PRTCFG_SHIFT)
+#define SAI_XCR1_PRTCFG_SET(x)	((x) << SAI_XCR1_PRTCFG_SHIFT)
+
+#define SAI_XCR1_DS_SHIFT	5
+#define SAI_XCR1_DS_MASK	GENMASK(7, SAI_XCR1_DS_SHIFT)
+#define SAI_XCR1_DS_SET(x)	((x) << SAI_XCR1_DS_SHIFT)
+
+#define SAI_XCR1_LSBFIRST_SHIFT	8
+#define SAI_XCR1_LSBFIRST	BIT(SAI_XCR1_LSBFIRST_SHIFT)
+#define SAI_XCR1_CKSTR_SHIFT	9
+#define SAI_XCR1_CKSTR		BIT(SAI_XCR1_CKSTR_SHIFT)
+
+#define SAI_XCR1_SYNCEN_SHIFT	10
+#define SAI_XCR1_SYNCEN_MASK	GENMASK(11, SAI_XCR1_SYNCEN_SHIFT)
+#define SAI_XCR1_SYNCEN_SET(x)	((x) << SAI_XCR1_SYNCEN_SHIFT)
+
+#define SAI_XCR1_MONO_SHIFT	12
+#define SAI_XCR1_MONO		BIT(SAI_XCR1_MONO_SHIFT)
+#define SAI_XCR1_OUTDRIV_SHIFT	13
+#define SAI_XCR1_OUTDRIV	BIT(SAI_XCR1_OUTDRIV_SHIFT)
+#define SAI_XCR1_SAIEN_SHIFT	16
+#define SAI_XCR1_SAIEN		BIT(SAI_XCR1_SAIEN_SHIFT)
+#define SAI_XCR1_DMAEN_SHIFT	17
+#define SAI_XCR1_DMAEN		BIT(SAI_XCR1_DMAEN_SHIFT)
+#define SAI_XCR1_NODIV_SHIFT	19
+#define SAI_XCR1_NODIV		BIT(SAI_XCR1_NODIV_SHIFT)
+
+#define SAI_XCR1_MCKDIV_SHIFT	20
+#define SAI_XCR1_MCKDIV_WIDTH	4
+#define SAI_XCR1_MCKDIV_MASK	GENMASK(24, SAI_XCR1_MCKDIV_SHIFT)
+#define SAI_XCR1_MCKDIV_SET(x)	((x) << SAI_XCR1_MCKDIV_SHIFT)
+#define SAI_XCR1_MCKDIV_MAX	((1 << SAI_XCR1_MCKDIV_WIDTH) - 1)
+
+#define SAI_XCR1_OSR_SHIFT	26
+#define SAI_XCR1_OSR		BIT(SAI_XCR1_OSR_SHIFT)
+
+/******************* Bit definition for SAI_XCR2 register *******************/
+#define SAI_XCR2_FTH_SHIFT	0
+#define SAI_XCR2_FTH_MASK	GENMASK(2, SAI_XCR2_FTH_SHIFT)
+#define SAI_XCR2_FTH_SET(x)	((x) << SAI_XCR2_FTH_SHIFT)
+
+#define SAI_XCR2_FFLUSH_SHIFT	3
+#define SAI_XCR2_FFLUSH		BIT(SAI_XCR2_FFLUSH_SHIFT)
+#define SAI_XCR2_TRIS_SHIFT	4
+#define SAI_XCR2_TRIS		BIT(SAI_XCR2_TRIS_SHIFT)
+#define SAI_XCR2_MUTE_SHIFT	5
+#define SAI_XCR2_MUTE		BIT(SAI_XCR2_MUTE_SHIFT)
+#define SAI_XCR2_MUTEVAL_SHIFT	6
+#define SAI_XCR2_MUTEVAL	BIT(SAI_XCR2_MUTEVAL_SHIFT)
+
+#define SAI_XCR2_MUTECNT_SHIFT	7
+#define SAI_XCR2_MUTECNT_MASK	GENMASK(12, SAI_XCR2_MUTECNT_SHIFT)
+#define SAI_XCR2_MUTECNT_SET(x)	((x) << SAI_XCR2_MUTECNT_SHIFT)
+
+#define SAI_XCR2_CPL_SHIFT	13
+#define SAI_XCR2_CPL		BIT(SAI_XCR2_CPL_SHIFT)
+
+#define SAI_XCR2_COMP_SHIFT	14
+#define SAI_XCR2_COMP_MASK	GENMASK(15, SAI_XCR2_COMP_SHIFT)
+#define SAI_XCR2_COMP_SET(x)	((x) << SAI_XCR2_COMP_SHIFT)
+
+/****************** Bit definition for SAI_XFRCR register *******************/
+#define SAI_XFRCR_FRL_SHIFT	0
+#define SAI_XFRCR_FRL_MASK	GENMASK(7, SAI_XFRCR_FRL_SHIFT)
+#define SAI_XFRCR_FRL_SET(x)	((x) << SAI_XFRCR_FRL_SHIFT)
+
+#define SAI_XFRCR_FSALL_SHIFT	8
+#define SAI_XFRCR_FSALL_MASK	GENMASK(14, SAI_XFRCR_FSALL_SHIFT)
+#define SAI_XFRCR_FSALL_SET(x)	((x) << SAI_XFRCR_FSALL_SHIFT)
+
+#define SAI_XFRCR_FSDEF_SHIFT	16
+#define SAI_XFRCR_FSDEF		BIT(SAI_XFRCR_FSDEF_SHIFT)
+#define SAI_XFRCR_FSPOL_SHIFT	17
+#define SAI_XFRCR_FSPOL		BIT(SAI_XFRCR_FSPOL_SHIFT)
+#define SAI_XFRCR_FSOFF_SHIFT	18
+#define SAI_XFRCR_FSOFF		BIT(SAI_XFRCR_FSOFF_SHIFT)
+
+/****************** Bit definition for SAI_XSLOTR register ******************/
+#define SAI_XSLOTR_FBOFF_SHIFT	0
+#define SAI_XSLOTR_FBOFF_MASK	GENMASK(4, SAI_XSLOTR_FBOFF_SHIFT)
+#define SAI_XSLOTR_FBOFF_SET(x)	((x) << SAI_XSLOTR_FBOFF_SHIFT)
+
+#define SAI_XSLOTR_SLOTSZ_SHIFT	6
+#define SAI_XSLOTR_SLOTSZ_MASK	GENMASK(7, SAI_XSLOTR_SLOTSZ_SHIFT)
+#define SAI_XSLOTR_SLOTSZ_SET(x)	((x) << SAI_XSLOTR_SLOTSZ_SHIFT)
+
+#define SAI_XSLOTR_NBSLOT_SHIFT 8
+#define SAI_XSLOTR_NBSLOT_MASK	GENMASK(11, SAI_XSLOTR_NBSLOT_SHIFT)
+#define SAI_XSLOTR_NBSLOT_SET(x) ((x) << SAI_XSLOTR_NBSLOT_SHIFT)
+
+#define SAI_XSLOTR_SLOTEN_SHIFT	16
+#define SAI_XSLOTR_SLOTEN_WIDTH	16
+#define SAI_XSLOTR_SLOTEN_MASK	GENMASK(31, SAI_XSLOTR_SLOTEN_SHIFT)
+#define SAI_XSLOTR_SLOTEN_SET(x) ((x) << SAI_XSLOTR_SLOTEN_SHIFT)
+
+/******************* Bit definition for SAI_XIMR register *******************/
+#define SAI_XIMR_OVRUDRIE	BIT(0)
+#define SAI_XIMR_MUTEDETIE	BIT(1)
+#define SAI_XIMR_WCKCFGIE	BIT(2)
+#define SAI_XIMR_FREQIE		BIT(3)
+#define SAI_XIMR_CNRDYIE	BIT(4)
+#define SAI_XIMR_AFSDETIE	BIT(5)
+#define SAI_XIMR_LFSDETIE	BIT(6)
+
+#define SAI_XIMR_SHIFT	0
+#define SAI_XIMR_MASK		GENMASK(6, SAI_XIMR_SHIFT)
+
+/******************** Bit definition for SAI_XSR register *******************/
+#define SAI_XSR_OVRUDR		BIT(0)
+#define SAI_XSR_MUTEDET		BIT(1)
+#define SAI_XSR_WCKCFG		BIT(2)
+#define SAI_XSR_FREQ		BIT(3)
+#define SAI_XSR_CNRDY		BIT(4)
+#define SAI_XSR_AFSDET		BIT(5)
+#define SAI_XSR_LFSDET		BIT(6)
+
+#define SAI_XSR_SHIFT	0
+#define SAI_XSR_MASK		GENMASK(6, SAI_XSR_SHIFT)
+
+/****************** Bit definition for SAI_XCLRFR register ******************/
+#define SAI_XCLRFR_COVRUDR	BIT(0)
+#define SAI_XCLRFR_CMUTEDET	BIT(1)
+#define SAI_XCLRFR_CWCKCFG	BIT(2)
+#define SAI_XCLRFR_CFREQ	BIT(3)
+#define SAI_XCLRFR_CCNRDY	BIT(4)
+#define SAI_XCLRFR_CAFSDET	BIT(5)
+#define SAI_XCLRFR_CLFSDET	BIT(6)
+
+#define SAI_XCLRFR_SHIFT	0
+#define SAI_XCLRFR_MASK		GENMASK(6, SAI_XCLRFR_SHIFT)
+
+enum stm32_sai_version {
+	SAI_STM32F4
+};
+
+/**
+ * struct stm32_sai_data - private data of SAI instance driver
+ * @pdev: device data pointer
+ * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz
+ * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz
+ * @version: SOC version
+ * @irq: SAI interrupt line
+ */
+struct stm32_sai_data {
+	struct platform_device *pdev;
+	struct clk *clk_x8k;
+	struct clk *clk_x11k;
+	int version;
+	int irq;
+};
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
new file mode 100644
index 0000000..24b8874
--- /dev/null
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -0,0 +1,881 @@
+/*
+ * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "stm32_sai.h"
+
+#define SAI_FREE_PROTOCOL	0x0
+
+#define SAI_SLOT_SIZE_AUTO	0x0
+#define SAI_SLOT_SIZE_16	0x1
+#define SAI_SLOT_SIZE_32	0x2
+
+#define SAI_DATASIZE_8		0x2
+#define SAI_DATASIZE_10		0x3
+#define SAI_DATASIZE_16		0x4
+#define SAI_DATASIZE_20		0x5
+#define SAI_DATASIZE_24		0x6
+#define SAI_DATASIZE_32		0x7
+
+#define STM_SAI_FIFO_SIZE	8
+#define STM_SAI_DAI_NAME_SIZE	15
+
+#define STM_SAI_IS_PLAYBACK(ip)	((ip)->dir == SNDRV_PCM_STREAM_PLAYBACK)
+#define STM_SAI_IS_CAPTURE(ip)	((ip)->dir == SNDRV_PCM_STREAM_CAPTURE)
+
+#define STM_SAI_A_ID		0x0
+#define STM_SAI_B_ID		0x1
+
+#define STM_SAI_BLOCK_NAME(x)	(((x)->id == STM_SAI_A_ID) ? "A" : "B")
+
+/**
+ * struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
+ * @pdev: device data pointer
+ * @regmap: SAI register map pointer
+ * @dma_params: dma configuration data for rx or tx channel
+ * @cpu_dai_drv: DAI driver data pointer
+ * @cpu_dai: DAI runtime data pointer
+ * @substream: PCM substream data pointer
+ * @pdata: SAI block parent data pointer
+ * @sai_ck: kernel clock feeding the SAI clock generator
+ * @phys_addr: SAI registers physical base address
+ * @mclk_rate: SAI block master clock frequency (Hz). set at init
+ * @id: SAI sub block id corresponding to sub-block A or B
+ * @dir: SAI block direction (playback or capture). set at init
+ * @master: SAI block mode flag. (true=master, false=slave) set at init
+ * @fmt: SAI block format. relevant only for custom protocols. set at init
+ * @sync: SAI block synchronization mode. (none, internal or external)
+ * @fs_length: frame synchronization length. depends on protocol settings
+ * @slots: rx or tx slot number
+ * @slot_width: rx or tx slot width in bits
+ * @slot_mask: rx or tx active slots mask. set at init or at runtime
+ * @data_size: PCM data width. corresponds to PCM substream width.
+ */
+struct stm32_sai_sub_data {
+	struct platform_device *pdev;
+	struct regmap *regmap;
+	struct snd_dmaengine_dai_dma_data dma_params;
+	struct snd_soc_dai_driver *cpu_dai_drv;
+	struct snd_soc_dai *cpu_dai;
+	struct snd_pcm_substream *substream;
+	struct stm32_sai_data *pdata;
+	struct clk *sai_ck;
+	dma_addr_t phys_addr;
+	unsigned int mclk_rate;
+	unsigned int id;
+	int dir;
+	bool master;
+	int fmt;
+	int sync;
+	int fs_length;
+	int slots;
+	int slot_width;
+	int slot_mask;
+	int data_size;
+};
+
+enum stm32_sai_fifo_th {
+	STM_SAI_FIFO_TH_EMPTY,
+	STM_SAI_FIFO_TH_QUARTER,
+	STM_SAI_FIFO_TH_HALF,
+	STM_SAI_FIFO_TH_3_QUARTER,
+	STM_SAI_FIFO_TH_FULL,
+};
+
+static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STM_SAI_CR1_REGX:
+	case STM_SAI_CR2_REGX:
+	case STM_SAI_FRCR_REGX:
+	case STM_SAI_SLOTR_REGX:
+	case STM_SAI_IMR_REGX:
+	case STM_SAI_SR_REGX:
+	case STM_SAI_CLRFR_REGX:
+	case STM_SAI_DR_REGX:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool stm32_sai_sub_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STM_SAI_DR_REGX:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STM_SAI_CR1_REGX:
+	case STM_SAI_CR2_REGX:
+	case STM_SAI_FRCR_REGX:
+	case STM_SAI_SLOTR_REGX:
+	case STM_SAI_IMR_REGX:
+	case STM_SAI_SR_REGX:
+	case STM_SAI_CLRFR_REGX:
+	case STM_SAI_DR_REGX:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config stm32_sai_sub_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = STM_SAI_DR_REGX,
+	.readable_reg = stm32_sai_sub_readable_reg,
+	.volatile_reg = stm32_sai_sub_volatile_reg,
+	.writeable_reg = stm32_sai_sub_writeable_reg,
+	.fast_io = true,
+};
+
+static irqreturn_t stm32_sai_isr(int irq, void *devid)
+{
+	struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid;
+	struct snd_pcm_substream *substream = sai->substream;
+	struct platform_device *pdev = sai->pdev;
+	unsigned int sr, imr, flags;
+	snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING;
+
+	regmap_read(sai->regmap, STM_SAI_IMR_REGX, &imr);
+	regmap_read(sai->regmap, STM_SAI_SR_REGX, &sr);
+
+	flags = sr & imr;
+	if (!flags)
+		return IRQ_NONE;
+
+	regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK,
+			   SAI_XCLRFR_MASK);
+
+	if (flags & SAI_XIMR_OVRUDRIE) {
+		dev_err(&pdev->dev, "IRQ %s\n",
+			STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun");
+		status = SNDRV_PCM_STATE_XRUN;
+	}
+
+	if (flags & SAI_XIMR_MUTEDETIE)
+		dev_dbg(&pdev->dev, "IRQ mute detected\n");
+
+	if (flags & SAI_XIMR_WCKCFGIE) {
+		dev_err(&pdev->dev, "IRQ wrong clock configuration\n");
+		status = SNDRV_PCM_STATE_DISCONNECTED;
+	}
+
+	if (flags & SAI_XIMR_CNRDYIE)
+		dev_err(&pdev->dev, "IRQ Codec not ready\n");
+
+	if (flags & SAI_XIMR_AFSDETIE) {
+		dev_err(&pdev->dev, "IRQ Anticipated frame synchro\n");
+		status = SNDRV_PCM_STATE_XRUN;
+	}
+
+	if (flags & SAI_XIMR_LFSDETIE) {
+		dev_err(&pdev->dev, "IRQ Late frame synchro\n");
+		status = SNDRV_PCM_STATE_XRUN;
+	}
+
+	if (status != SNDRV_PCM_STATE_RUNNING) {
+		snd_pcm_stream_lock(substream);
+		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+		snd_pcm_stream_unlock(substream);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai,
+				int clk_id, unsigned int freq, int dir)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+
+	if ((dir == SND_SOC_CLOCK_OUT) && sai->master) {
+		sai->mclk_rate = freq;
+		dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq);
+	}
+
+	return 0;
+}
+
+static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+				      u32 rx_mask, int slots, int slot_width)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int slotr, slotr_mask, slot_size;
+
+	dev_dbg(cpu_dai->dev, "Masks tx/rx:%#x/%#x, slots:%d, width:%d\n",
+		tx_mask, rx_mask, slots, slot_width);
+
+	switch (slot_width) {
+	case 16:
+		slot_size = SAI_SLOT_SIZE_16;
+		break;
+	case 32:
+		slot_size = SAI_SLOT_SIZE_32;
+		break;
+	default:
+		slot_size = SAI_SLOT_SIZE_AUTO;
+		break;
+	}
+
+	slotr = SAI_XSLOTR_SLOTSZ_SET(slot_size) |
+		SAI_XSLOTR_NBSLOT_SET(slots - 1);
+	slotr_mask = SAI_XSLOTR_SLOTSZ_MASK | SAI_XSLOTR_NBSLOT_MASK;
+
+	/* tx/rx mask set in machine init, if slot number defined in DT */
+	if (STM_SAI_IS_PLAYBACK(sai)) {
+		sai->slot_mask = tx_mask;
+		slotr |= SAI_XSLOTR_SLOTEN_SET(tx_mask);
+	}
+
+	if (STM_SAI_IS_CAPTURE(sai)) {
+		sai->slot_mask = rx_mask;
+		slotr |= SAI_XSLOTR_SLOTEN_SET(rx_mask);
+	}
+
+	slotr_mask |= SAI_XSLOTR_SLOTEN_MASK;
+
+	regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, slotr_mask, slotr);
+
+	sai->slot_width = slot_width;
+	sai->slots = slots;
+
+	return 0;
+}
+
+static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int cr1 = 0, frcr = 0;
+	int cr1_mask = 0, frcr_mask = 0;
+	int ret;
+
+	dev_dbg(cpu_dai->dev, "fmt %x\n", fmt);
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	/* SCK active high for all protocols */
+	case SND_SOC_DAIFMT_I2S:
+		cr1 |= SAI_XCR1_CKSTR;
+		frcr |= SAI_XFRCR_FSOFF | SAI_XFRCR_FSDEF;
+		break;
+	/* Left justified */
+	case SND_SOC_DAIFMT_MSB:
+		frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF;
+		break;
+	/* Right justified */
+	case SND_SOC_DAIFMT_LSB:
+		frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		frcr |= SAI_XFRCR_FSPOL;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unsupported protocol %#x\n",
+			fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+		return -EINVAL;
+	}
+
+	cr1_mask |= SAI_XCR1_PRTCFG_MASK | SAI_XCR1_CKSTR;
+	frcr_mask |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF |
+		     SAI_XFRCR_FSDEF;
+
+	/* DAI clock strobing. Invert setting previously set */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		cr1 ^= SAI_XCR1_CKSTR;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		frcr ^= SAI_XFRCR_FSPOL;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		/* Invert fs & sck */
+		cr1 ^= SAI_XCR1_CKSTR;
+		frcr ^= SAI_XFRCR_FSPOL;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unsupported strobing %#x\n",
+			fmt & SND_SOC_DAIFMT_INV_MASK);
+		return -EINVAL;
+	}
+	cr1_mask |= SAI_XCR1_CKSTR;
+	frcr_mask |= SAI_XFRCR_FSPOL;
+
+	regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr);
+
+	/* DAI clock master masks */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		/* codec is master */
+		cr1 |= SAI_XCR1_SLAVE;
+		sai->master = false;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		sai->master = true;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unsupported mode %#x\n",
+			fmt & SND_SOC_DAIFMT_MASTER_MASK);
+		return -EINVAL;
+	}
+	cr1_mask |= SAI_XCR1_SLAVE;
+
+	ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
+	if (ret < 0) {
+		dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+		return ret;
+	}
+
+	sai->fmt = fmt;
+
+	return 0;
+}
+
+static int stm32_sai_startup(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int imr, cr2, ret;
+
+	sai->substream = substream;
+
+	ret = clk_prepare_enable(sai->sai_ck);
+	if (ret < 0) {
+		dev_err(cpu_dai->dev, "Failed to enable clock: %d\n", ret);
+		return ret;
+	}
+
+	/* Enable ITs */
+	regmap_update_bits(sai->regmap, STM_SAI_SR_REGX,
+			   SAI_XSR_MASK, (unsigned int)~SAI_XSR_MASK);
+
+	regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX,
+			   SAI_XCLRFR_MASK, SAI_XCLRFR_MASK);
+
+	imr = SAI_XIMR_OVRUDRIE;
+	if (STM_SAI_IS_CAPTURE(sai)) {
+		regmap_read(sai->regmap, STM_SAI_CR2_REGX, &cr2);
+		if (cr2 & SAI_XCR2_MUTECNT_MASK)
+			imr |= SAI_XIMR_MUTEDETIE;
+	}
+
+	if (sai->master)
+		imr |= SAI_XIMR_WCKCFGIE;
+	else
+		imr |= SAI_XIMR_AFSDETIE | SAI_XIMR_LFSDETIE;
+
+	regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX,
+			   SAI_XIMR_MASK, imr);
+
+	return 0;
+}
+
+static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai,
+				struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int cr1, cr1_mask, ret;
+	int fth = STM_SAI_FIFO_TH_HALF;
+
+	/* FIFO config */
+	regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX,
+			   SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK,
+			   SAI_XCR2_FFLUSH | SAI_XCR2_FTH_SET(fth));
+
+	/* Mode, data format and channel config */
+	cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL);
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_8);
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_16);
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_32);
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Data format not supported");
+		return -EINVAL;
+	}
+	cr1_mask = SAI_XCR1_DS_MASK | SAI_XCR1_PRTCFG_MASK;
+
+	cr1_mask |= SAI_XCR1_RX_TX;
+	if (STM_SAI_IS_CAPTURE(sai))
+		cr1 |= SAI_XCR1_RX_TX;
+
+	cr1_mask |= SAI_XCR1_MONO;
+	if ((sai->slots == 2) && (params_channels(params) == 1))
+		cr1 |= SAI_XCR1_MONO;
+
+	ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
+	if (ret < 0) {
+		dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+		return ret;
+	}
+
+	/* DMA config */
+	sai->dma_params.maxburst = STM_SAI_FIFO_SIZE * fth / sizeof(u32);
+	snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&sai->dma_params);
+
+	return 0;
+}
+
+static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int slotr, slot_sz;
+
+	regmap_read(sai->regmap, STM_SAI_SLOTR_REGX, &slotr);
+
+	/*
+	 * If SLOTSZ is set to auto in SLOTR, align slot width on data size
+	 * By default slot width = data size, if not forced from DT
+	 */
+	slot_sz = slotr & SAI_XSLOTR_SLOTSZ_MASK;
+	if (slot_sz == SAI_XSLOTR_SLOTSZ_SET(SAI_SLOT_SIZE_AUTO))
+		sai->slot_width = sai->data_size;
+
+	if (sai->slot_width < sai->data_size) {
+		dev_err(cpu_dai->dev,
+			"Data size %d larger than slot width\n",
+			sai->data_size);
+		return -EINVAL;
+	}
+
+	/* Slot number is set to 2, if not specified in DT */
+	if (!sai->slots)
+		sai->slots = 2;
+
+	/* The number of slots in the audio frame is equal to NBSLOT[3:0] + 1*/
+	regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX,
+			   SAI_XSLOTR_NBSLOT_MASK,
+			   SAI_XSLOTR_NBSLOT_SET((sai->slots - 1)));
+
+	/* Set default slots mask if not already set from DT */
+	if (!(slotr & SAI_XSLOTR_SLOTEN_MASK)) {
+		sai->slot_mask = (1 << sai->slots) - 1;
+		regmap_update_bits(sai->regmap,
+				   STM_SAI_SLOTR_REGX, SAI_XSLOTR_SLOTEN_MASK,
+				   SAI_XSLOTR_SLOTEN_SET(sai->slot_mask));
+	}
+
+	dev_dbg(cpu_dai->dev, "Slots %d, slot width %d\n",
+		sai->slots, sai->slot_width);
+
+	return 0;
+}
+
+static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int fs_active, offset, format;
+	int frcr, frcr_mask;
+
+	format = sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+	sai->fs_length = sai->slot_width * sai->slots;
+
+	fs_active = sai->fs_length / 2;
+	if ((format == SND_SOC_DAIFMT_DSP_A) ||
+	    (format == SND_SOC_DAIFMT_DSP_B))
+		fs_active = 1;
+
+	frcr = SAI_XFRCR_FRL_SET((sai->fs_length - 1));
+	frcr |= SAI_XFRCR_FSALL_SET((fs_active - 1));
+	frcr_mask = SAI_XFRCR_FRL_MASK | SAI_XFRCR_FSALL_MASK;
+
+	dev_dbg(cpu_dai->dev, "Frame length %d, frame active %d\n",
+		sai->fs_length, fs_active);
+
+	regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr);
+
+	if ((sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_LSB) {
+		offset = sai->slot_width - sai->data_size;
+
+		regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX,
+				   SAI_XSLOTR_FBOFF_MASK,
+				   SAI_XSLOTR_FBOFF_SET(offset));
+	}
+}
+
+static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
+				     struct snd_pcm_hw_params *params)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int cr1, mask, div = 0;
+	int sai_clk_rate, ret;
+
+	if (!sai->mclk_rate) {
+		dev_err(cpu_dai->dev, "Mclk rate is null\n");
+		return -EINVAL;
+	}
+
+	if (!(params_rate(params) % 11025))
+		clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k);
+	else
+		clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k);
+	sai_clk_rate = clk_get_rate(sai->sai_ck);
+
+	/*
+	 * mclk_rate = 256 * fs
+	 * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
+	 * MCKDIV = sai_ck / (2 * mclk_rate) otherwise
+	 */
+	if (2 * sai_clk_rate >= 3 * sai->mclk_rate)
+		div = DIV_ROUND_CLOSEST(sai_clk_rate, 2 * sai->mclk_rate);
+
+	if (div > SAI_XCR1_MCKDIV_MAX) {
+		dev_err(cpu_dai->dev, "Divider %d out of range\n", div);
+		return -EINVAL;
+	}
+	dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div);
+
+	mask = SAI_XCR1_MCKDIV_MASK;
+	cr1 = SAI_XCR1_MCKDIV_SET(div);
+	ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
+	if (ret < 0) {
+		dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int stm32_sai_hw_params(struct snd_pcm_substream *substream,
+			       struct snd_pcm_hw_params *params,
+			       struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int ret;
+
+	sai->data_size = params_width(params);
+
+	ret = stm32_sai_set_slots(cpu_dai);
+	if (ret < 0)
+		return ret;
+	stm32_sai_set_frame(cpu_dai);
+
+	ret = stm32_sai_set_config(cpu_dai, substream, params);
+	if (ret)
+		return ret;
+
+	if (sai->master)
+		ret = stm32_sai_configure_clock(cpu_dai, params);
+
+	return ret;
+}
+
+static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd,
+			     struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int ret;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		dev_dbg(cpu_dai->dev, "Enable DMA and SAI\n");
+
+		regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+				   SAI_XCR1_DMAEN, SAI_XCR1_DMAEN);
+
+		/* Enable SAI */
+		ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+					 SAI_XCR1_SAIEN, SAI_XCR1_SAIEN);
+		if (ret < 0)
+			dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_STOP:
+		dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n");
+
+		regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+				   SAI_XCR1_DMAEN,
+				   (unsigned int)~SAI_XCR1_DMAEN);
+
+		ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+					 SAI_XCR1_SAIEN,
+					 (unsigned int)~SAI_XCR1_SAIEN);
+		if (ret < 0)
+			dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static void stm32_sai_shutdown(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+
+	regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0);
+
+	clk_disable_unprepare(sai->sai_ck);
+	sai->substream = NULL;
+}
+
+static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev);
+
+	sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX);
+	sai->dma_params.maxburst = 1;
+	/* Buswidth will be set by framework at runtime */
+	sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+
+	if (STM_SAI_IS_PLAYBACK(sai))
+		snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params, NULL);
+	else
+		snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = {
+	.set_sysclk	= stm32_sai_set_sysclk,
+	.set_fmt	= stm32_sai_set_dai_fmt,
+	.set_tdm_slot	= stm32_sai_set_dai_tdm_slot,
+	.startup	= stm32_sai_startup,
+	.hw_params	= stm32_sai_hw_params,
+	.trigger	= stm32_sai_trigger,
+	.shutdown	= stm32_sai_shutdown,
+};
+
+static const struct snd_pcm_hardware stm32_sai_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP,
+	.buffer_bytes_max = 8 * PAGE_SIZE,
+	.period_bytes_min = 1024, /* 5ms at 48kHz */
+	.period_bytes_max = PAGE_SIZE,
+	.periods_min = 2,
+	.periods_max = 8,
+};
+
+static struct snd_soc_dai_driver stm32_sai_playback_dai[] = {
+{
+		.probe = stm32_sai_dai_probe,
+		.id = 1, /* avoid call to fmt_single_name() */
+		.playback = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.rates = SNDRV_PCM_RATE_CONTINUOUS,
+			/* DMA does not support 24 bits transfers */
+			.formats =
+				SNDRV_PCM_FMTBIT_S8 |
+				SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &stm32_sai_pcm_dai_ops,
+	}
+};
+
+static struct snd_soc_dai_driver stm32_sai_capture_dai[] = {
+{
+		.probe = stm32_sai_dai_probe,
+		.id = 1, /* avoid call to fmt_single_name() */
+		.capture = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.rates = SNDRV_PCM_RATE_CONTINUOUS,
+			/* DMA does not support 24 bits transfers */
+			.formats =
+				SNDRV_PCM_FMTBIT_S8 |
+				SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &stm32_sai_pcm_dai_ops,
+	}
+};
+
+static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = {
+	.pcm_hardware	= &stm32_sai_pcm_hw,
+	.prepare_slave_config	= snd_dmaengine_pcm_prepare_slave_config,
+};
+
+static const struct snd_soc_component_driver stm32_component = {
+	.name = "stm32-sai",
+};
+
+static const struct of_device_id stm32_sai_sub_ids[] = {
+	{ .compatible = "st,stm32-sai-sub-a",
+	  .data = (void *)STM_SAI_A_ID},
+	{ .compatible = "st,stm32-sai-sub-b",
+	  .data = (void *)STM_SAI_B_ID},
+	{}
+};
+MODULE_DEVICE_TABLE(of, stm32_sai_sub_ids);
+
+static int stm32_sai_sub_parse_of(struct platform_device *pdev,
+				  struct stm32_sai_sub_data *sai)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct resource *res;
+	void __iomem *base;
+
+	if (!np)
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	sai->phys_addr = res->start;
+	sai->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+					    &stm32_sai_sub_regmap_config);
+
+	/* Get direction property */
+	if (of_property_match_string(np, "dma-names", "tx") >= 0) {
+		sai->dir = SNDRV_PCM_STREAM_PLAYBACK;
+	} else if (of_property_match_string(np, "dma-names", "rx") >= 0) {
+		sai->dir = SNDRV_PCM_STREAM_CAPTURE;
+	} else {
+		dev_err(&pdev->dev, "Unsupported direction\n");
+		return -EINVAL;
+	}
+
+	sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck");
+	if (IS_ERR(sai->sai_ck)) {
+		dev_err(&pdev->dev, "Missing kernel clock sai_ck\n");
+		return PTR_ERR(sai->sai_ck);
+	}
+
+	return 0;
+}
+
+static int stm32_sai_sub_dais_init(struct platform_device *pdev,
+				   struct stm32_sai_sub_data *sai)
+{
+	sai->cpu_dai_drv = devm_kzalloc(&pdev->dev,
+					sizeof(struct snd_soc_dai_driver),
+					GFP_KERNEL);
+	if (!sai->cpu_dai_drv)
+		return -ENOMEM;
+
+	sai->cpu_dai_drv->name = dev_name(&pdev->dev);
+	if (STM_SAI_IS_PLAYBACK(sai)) {
+		memcpy(sai->cpu_dai_drv, &stm32_sai_playback_dai,
+		       sizeof(stm32_sai_playback_dai));
+		sai->cpu_dai_drv->playback.stream_name = sai->cpu_dai_drv->name;
+	} else {
+		memcpy(sai->cpu_dai_drv, &stm32_sai_capture_dai,
+		       sizeof(stm32_sai_capture_dai));
+		sai->cpu_dai_drv->capture.stream_name = sai->cpu_dai_drv->name;
+	}
+
+	return 0;
+}
+
+static int stm32_sai_sub_probe(struct platform_device *pdev)
+{
+	struct stm32_sai_sub_data *sai;
+	const struct of_device_id *of_id;
+	int ret;
+
+	sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+	if (!sai)
+		return -ENOMEM;
+
+	of_id = of_match_device(stm32_sai_sub_ids, &pdev->dev);
+	if (!of_id)
+		return -EINVAL;
+	sai->id = (uintptr_t)of_id->data;
+
+	sai->pdev = pdev;
+	platform_set_drvdata(pdev, sai);
+
+	sai->pdata = dev_get_drvdata(pdev->dev.parent);
+	if (!sai->pdata) {
+		dev_err(&pdev->dev, "Parent device data not available\n");
+		return -EINVAL;
+	}
+
+	ret = stm32_sai_sub_parse_of(pdev, sai);
+	if (ret)
+		return ret;
+
+	ret = stm32_sai_sub_dais_init(pdev, sai);
+	if (ret)
+		return ret;
+
+	ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr,
+			       IRQF_SHARED, dev_name(&pdev->dev), sai);
+	if (ret) {
+		dev_err(&pdev->dev, "IRQ request returned %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_snd_soc_register_component(&pdev->dev, &stm32_component,
+					      sai->cpu_dai_drv, 1);
+	if (ret)
+		return ret;
+
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+					      &stm32_sai_pcm_config, 0);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register pcm dma\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver stm32_sai_sub_driver = {
+	.driver = {
+		.name = "st,stm32-sai-sub",
+		.of_match_table = stm32_sai_sub_ids,
+	},
+	.probe = stm32_sai_sub_probe,
+};
+
+module_platform_driver(stm32_sai_sub_driver);
+
+MODULE_DESCRIPTION("STM32 Soc SAI sub-block Interface");
+MODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>");
+MODULE_ALIAS("platform:st,stm32-sai-sub");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related


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