* [PATCH v3] arm64: SMMU-v2: Workaround for Cavium ThunderX erratum 28168
From: Geetha sowjanya @ 2016-12-20 11:06 UTC (permalink / raw)
To: linux-arm-kernel
From: Tirumalesh Chalamarla <tchalamarla@caviumnetworks.com>
This patch implements Cavium ThunderX erratum 28168.
PCI requires stores complete in order. Due to erratum #28168
PCI-inbound MSI-X store to the interrupt controller are delivered
to the interrupt controller before older PCI-inbound memory stores
are committed.
Doing a sync on SMMU will make sure all prior data transfers are
completed before invoking ISR.
changes from v2:
- added entry in Documentation/arm64/silicon-errata.txt
- moved registration of the preflow_handler into
msi_domain_ops.msi_finish() handler.
- create linux/arm.smmu.h to expose smmu API.
Signed-off-by: Tirumalesh Chalamarla <Tirumalesh.Chalamarla@cavium.com>
---
Documentation/arm64/silicon-errata.txt | 1 +
arch/arm64/Kconfig | 12 ++++++++
arch/arm64/include/asm/cpucaps.h | 3 +-
arch/arm64/kernel/cpu_errata.c | 16 +++++++++++
drivers/iommu/arm-smmu.c | 22 +++++++++++++++
drivers/irqchip/irq-gic-v3-its-pci-msi.c | 48 +++++++++++++++++++++++++++++++-
include/linux/arm-smmu.h | 8 ++++++
7 files changed, 108 insertions(+), 2 deletions(-)
create mode 100644 include/linux/arm-smmu.h
diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt
index 405da11..1311f90 100644
--- a/Documentation/arm64/silicon-errata.txt
+++ b/Documentation/arm64/silicon-errata.txt
@@ -61,5 +61,6 @@ stable kernels.
| Cavium | ThunderX GICv3 | #23154 | CAVIUM_ERRATUM_23154 |
| Cavium | ThunderX Core | #27456 | CAVIUM_ERRATUM_27456 |
| Cavium | ThunderX SMMUv2 | #27704 | N/A |
+| Cavium | ThunderX PCI | #28168 | CAVIUM_ERRATUM_28168 |
| | | | |
| Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 969ef88..cb647f4 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -474,6 +474,18 @@ config CAVIUM_ERRATUM_27456
If unsure, say Y.
+config CAVIUM_ERRATUM_28168
+ bool "Cavium erratum 28168: Make sure DMA data transfer is done before MSIX"
+ depends on ARM64 && ARM_SMMU
+ default y
+ select IRQ_PREFLOW_FASTEOI
+ help
+ Due to erratum #28168 PCI-inbound MSI-X store to the interrupt
+ controller are delivered to the interrupt controller before older
+ PCI-inbound memory stores are committed. Doing a sync on SMMU
+ will make sure all prior data transfers are done before invoking ISR.
+
+ If unsure, say Y.
endmenu
diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
index 87b4465..6975a01 100644
--- a/arch/arm64/include/asm/cpucaps.h
+++ b/arch/arm64/include/asm/cpucaps.h
@@ -34,7 +34,8 @@
#define ARM64_HAS_32BIT_EL0 13
#define ARM64_HYP_OFFSET_LOW 14
#define ARM64_MISMATCHED_CACHE_LINE_SIZE 15
+#define ARM64_WORKAROUND_CAVIUM_28168 16
-#define ARM64_NCAPS 16
+#define ARM64_NCAPS 17
#endif /* __ASM_CPUCAPS_H */
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index b75e917..fac6d74 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -123,6 +123,22 @@ static int cpu_enable_trap_ctr_access(void *__unused)
MIDR_RANGE(MIDR_THUNDERX_81XX, 0x00, 0x00),
},
#endif
+#ifdef CONFIG_CAVIUM_ERRATUM_28168
+ {
+ /* Cavium ThunderX, T88 pass 1.x - 2.1 */
+ .desc = "Cavium erratum 28168",
+ .capability = ARM64_WORKAROUND_CAVIUM_28168,
+ MIDR_RANGE(MIDR_THUNDERX, 0x00,
+ (1 << MIDR_VARIANT_SHIFT) | 1),
+ },
+ {
+ /* Cavium ThunderX, T81 pass 1.0 */
+ .desc = "Cavium erratum 28168",
+ .capability = ARM64_WORKAROUND_CAVIUM_28168,
+ MIDR_RANGE(MIDR_THUNDERX_81XX, 0x00, 0x00),
+ },
+#endif
+
{
.desc = "Mismatched cache line size",
.capability = ARM64_MISMATCHED_CACHE_LINE_SIZE,
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 06167da..451b393 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -49,6 +49,8 @@
#include <linux/spinlock.h>
#include <linux/amba/bus.h>
+#include <linux/arm-smmu.h>
+
#include "io-pgtable.h"
@@ -577,6 +579,26 @@ static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
}
}
+#ifdef CONFIG_CAVIUM_ERRATUM_28168
+/*
+ * Cavium ThunderX erratum 28168
+ *
+ * Due to erratum #28168 PCI-inbound MSI-X store to the interrupt
+ * controller are delivered to the interrupt controller before older
+ * PCI-inbound memory stores are committed. Doing a sync on SMMU
+ * will make sure all prior data transfers are completed before
+ * invoking ISR.
+ *
+ */
+void dev_smmu_tlb_sync(struct device *dev)
+{
+ struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+ struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
+
+ if (smmu)
+ __arm_smmu_tlb_sync(smmu);
+}
+#endif
static void arm_smmu_tlb_sync(void *cookie)
{
struct arm_smmu_domain *smmu_domain = cookie;
diff --git a/drivers/irqchip/irq-gic-v3-its-pci-msi.c b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
index aee1c60..36ce696 100644
--- a/drivers/irqchip/irq-gic-v3-its-pci-msi.c
+++ b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
@@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_pci.h>
+#include <linux/arm-smmu.h>
static void its_mask_msi_irq(struct irq_data *d)
{
@@ -86,11 +87,53 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
/* ITS specific DeviceID, as the core ITS ignores dev. */
info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
-
+ info->scratchpad[1].ptr = &pdev->dev;
return msi_info->ops->msi_prepare(domain->parent,
dev, dev_alias.count, info);
}
+#ifdef CONFIG_CAVIUM_ERRATUM_28168
+#define THUNDER_PME_DEVICE_ID 0xA100
+
+static void cavium_irq_preflow_handler(struct irq_data *data)
+{
+ struct pci_dev *pdev;
+
+ pdev = msi_desc_to_pci_dev(irq_data_get_msi_desc(data));
+ dev_smmu_tlb_sync(&pdev->dev);
+}
+static void cavium_its_pci_msi_finish(msi_alloc_info_t *info, int ret)
+{
+ struct device *dev = info->scratchpad[1].ptr;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct device *parent_dev;
+ struct msi_desc *desc;
+
+ if (ret)
+ return;
+
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) {
+ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) {
+ pdev = to_pci_dev(parent_dev);
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT)
+ continue;
+
+ if (pdev->device == THUNDER_PME_DEVICE_ID)
+ for_each_pci_msi_entry(desc, to_pci_dev(dev))
+ __irq_set_preflow_handler(desc->irq, cavium_irq_preflow_handler);
+ break;
+ }
+ }
+}
+#else
+static void cavium_irq_preflow_handler(struct irq_data *data)
+{
+}
+static void cavium_its_pci_msi_finish(msi_alloc_info_t *info, int ret)
+{
+}
+#endif
+
static struct msi_domain_ops its_pci_msi_ops = {
.msi_prepare = its_pci_msi_prepare,
};
@@ -118,6 +161,9 @@ static int __init its_pci_msi_init_one(struct fwnode_handle *handle,
return -ENXIO;
}
+ if (cpus_have_cap(ARM64_WORKAROUND_CAVIUM_28168))
+ its_pci_msi_ops.msi_finish = cavium_its_pci_msi_finish;
+
if (!pci_msi_create_irq_domain(handle, &its_pci_msi_domain_info,
parent)) {
pr_err("%s: Unable to create PCI domain\n", name);
diff --git a/include/linux/arm-smmu.h b/include/linux/arm-smmu.h
new file mode 100644
index 0000000..d3f5dff
--- /dev/null
+++ b/include/linux/arm-smmu.h
@@ -0,0 +1,8 @@
+#ifndef _ARM_SMMU_H
+#define _ARM_SMMU_H
+
+#include<linux/pci.h>
+
+extern void dev_smmu_tlb_sync(struct device *dev);
+
+#endif /* _ARM_SMMU_H */
--
1.9.1
^ permalink raw reply related
* [linux-sunxi] Problems to Allwinner H3's eFUSE/SID
From: Bernhard Nortmann @ 2016-12-20 11:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <136061482160932@web34j.yandex.ru>
Hi all!
This is rather interesting stuff. Actually it's possible to find partial
documentation for this method (of reading SID/efuse values) even for older
SoCs. There's an early version of the A20 User Manual (Revision 1.0 -
Feb. 18, 2013) available on the net, where chapter 1.18 describes the
registers involved. This section seems to have been removed in later
editions. (Search for the file A20_UM-V1.020130322.pdf)
The U-Boot code that Allwinner published also has source files for various
SoCs that contain corresponding implementations of `sid_program_key()` and
`sid_read_key()` (in efuse.c),
see https://github.com/allwinner-zh/bootloader/search?q=sid_read_key.
Regards, B. Nortmann
Am 19.12.2016 um 16:22 schrieb Icenowy Zheng:
> Hi everyone,
>
> Today, I and KotCzarny on IRC of linux-sunxi found a problem in the SID
> controller of H3 (incl. H2+).
>
> See https://irclog.whitequark.org/linux-sunxi/2016-12-19 .
>
> Two read method of the H3 eFUSE is used in the BSP: by register accessing, or
> directly access 0x01c14200.
>
> From http://linux-sunxi.org/SID_Register_Guide we can see a difference between
> the H3 SIDs read out by sunxi-fel and the H3 SIDs read out by devmem2 (in
> legacy kernel).
>
> According to the source of H2+ BSP[1], H2+ and H3 can be differed by the last
> byte of the first word of SID. (0x42 and 0x83 is H2+, 0x00 and 0x81 is H3,
> 0x58 is H3D (currently not known SoC) )
>
> However, all the SIDs retrieved by `sunxi-fel sid`, both H2+ and H3, start
> with 0x02004620, which do not match this rule.
>
> The readout by devmem2 is satisfying this rule: their first word is
> 0x02c00081, matches H3.
>
> Then I found the SID-reading code from BSP U-Boot[2], which is based on
> register operations. With this kind of code (I wrote one prototype in
> userspace with /dev/mem), I got "02c00081 74004620 50358720 3c27048e" on
> my Orange Pi One. ("02004620 74358720 5027048e 3c0000c3" with sunxi-fel sid)
> And, after accessing to the SID by registers, the value of *0x01c14200 become
> also "02c00081".
>
> With direct access to 0x01c14200 after boot with mainline kernel, I got also
> "02004620".
>
> Then I altered the program to do the register operations with sunxi-fel, the
> result is also "02c00081", and changed `sunxi-fel sid` result to "02c00081".
>
> Summary:
>
> +-----------------------------------------------+----------------+
> | Read situation | The first word |
> +-----------------------------------------------+----------------+
> | Direct read by sunxi-fel | 02004620 |
> | Direct read in mainline /dev/mem | 02004620 |
> | Direct read in legacy /dev/mem | 02c00081 |
> | Register access in FEL | 02c00081 |
> | Register access in mainline | 02c00081 |
> | Direct read after register access in FEL | 02c00081 |
> | Direct read after register access in mainline | 02c00081 |
> +-----------------------------------------------+----------------+
>
> According to some facts:
> - The register based access to SID is weird: it needs ~5 register
> operations per word of SID.
> - Reading via register access will change the value when reading by accessing
> 0x01c14200.
> - In the u-boot code[2] there's some functions which read out the SID by
> registers and then abandoned the value.
> - This mismatch do not exist on A64.
>
> I think that: Allwinner designed a "cache" to the SID to make the simplify the
> code to read it, and it automatically loaded the cache when booting; however,
> when doing first cache on H3, some byte shifts occured, and the value become
> wrong. A manual read on H3 can make the cache right again. This is a silicon
> bug, and fixed in A64.
>
> This raises a problem: currently many systems has used the misread SID value to
> generated lots of MAC addresses, and workaround this SID bug will change them.
>
> However, if this bug is not workarounded, the sun8i-ths driver won't work well
> (as some calibartion value lies in eFUSE). I think some early user of this
> driver has already experienced bad readout value.
> (The calibration value differs on my opi1 and KotCzarny's opipc)
>
> And many wrong SID values have been generated by `sunxi-fel sid`. (Although I
> think sunxi-fel must have the workaround)
>
> Note: in this email, "SID" and "eFUSE" both indicate the controller on H3/A64
> at 0x01c14000, which is a OTP memory implemented by eFUSE technique.
>
> Furthermore, A83T may also have this problem, testers are welcome!
>
> [1] http://filez.zoobab.com/allwinner/h2/201609022/lichee/linux-3.4/arch/arm/mach-sunxi/sun8i.c
> [2] http://filez.zoobab.com/allwinner/h2/201609022/lichee/brandy/u-boot-2011.09/arch/arm/cpu/armv7/sun8iw7/efuse.c
>
> Experiments:
> - https://gist.github.com/Icenowy/2f4859ab1bc05814522fc7445179a8c9
> A SID readout shell script via FEL with register access.
> - https://31.135.195.151:20281/d/efuse/
> A SID readout program via /dev/mem with register access by KotCzarny.
> (with statically compiled binary)
>
> Regards,
> Icenowy
>
^ permalink raw reply
* [PATCH v3] arm64: SMMU-v2: Workaround for Cavium ThunderX erratum 28168
From: Marc Zyngier @ 2016-12-20 11:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482231993-27571-1-git-send-email-gakula@caviumnetworks.com>
Geetha,
On 20/12/16 11:06, Geetha sowjanya wrote:
> From: Tirumalesh Chalamarla <tchalamarla@caviumnetworks.com>
>
> This patch implements Cavium ThunderX erratum 28168.
>
> PCI requires stores complete in order. Due to erratum #28168
> PCI-inbound MSI-X store to the interrupt controller are delivered
> to the interrupt controller before older PCI-inbound memory stores
> are committed.
> Doing a sync on SMMU will make sure all prior data transfers are
> completed before invoking ISR.
>
> changes from v2:
> - added entry in Documentation/arm64/silicon-errata.txt
> - moved registration of the preflow_handler into
> msi_domain_ops.msi_finish() handler.
> - create linux/arm.smmu.h to expose smmu API.
>
> Signed-off-by: Tirumalesh Chalamarla <Tirumalesh.Chalamarla@cavium.com>
Where is your SoB? This is a requirement if you're picking a patch from
someone else.
> ---
> Documentation/arm64/silicon-errata.txt | 1 +
> arch/arm64/Kconfig | 12 ++++++++
> arch/arm64/include/asm/cpucaps.h | 3 +-
> arch/arm64/kernel/cpu_errata.c | 16 +++++++++++
> drivers/iommu/arm-smmu.c | 22 +++++++++++++++
> drivers/irqchip/irq-gic-v3-its-pci-msi.c | 48 +++++++++++++++++++++++++++++++-
> include/linux/arm-smmu.h | 8 ++++++
> 7 files changed, 108 insertions(+), 2 deletions(-)
> create mode 100644 include/linux/arm-smmu.h
>
> diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt
> index 405da11..1311f90 100644
> --- a/Documentation/arm64/silicon-errata.txt
> +++ b/Documentation/arm64/silicon-errata.txt
> @@ -61,5 +61,6 @@ stable kernels.
> | Cavium | ThunderX GICv3 | #23154 | CAVIUM_ERRATUM_23154 |
> | Cavium | ThunderX Core | #27456 | CAVIUM_ERRATUM_27456 |
> | Cavium | ThunderX SMMUv2 | #27704 | N/A |
> +| Cavium | ThunderX PCI | #28168 | CAVIUM_ERRATUM_28168 |
> | | | | |
> | Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 969ef88..cb647f4 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -474,6 +474,18 @@ config CAVIUM_ERRATUM_27456
>
> If unsure, say Y.
>
> +config CAVIUM_ERRATUM_28168
> + bool "Cavium erratum 28168: Make sure DMA data transfer is done before MSIX"
> + depends on ARM64 && ARM_SMMU
> + default y
> + select IRQ_PREFLOW_FASTEOI
> + help
> + Due to erratum #28168 PCI-inbound MSI-X store to the interrupt
> + controller are delivered to the interrupt controller before older
> + PCI-inbound memory stores are committed. Doing a sync on SMMU
> + will make sure all prior data transfers are done before invoking ISR.
> +
> + If unsure, say Y.
> endmenu
>
>
> diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
> index 87b4465..6975a01 100644
> --- a/arch/arm64/include/asm/cpucaps.h
> +++ b/arch/arm64/include/asm/cpucaps.h
> @@ -34,7 +34,8 @@
> #define ARM64_HAS_32BIT_EL0 13
> #define ARM64_HYP_OFFSET_LOW 14
> #define ARM64_MISMATCHED_CACHE_LINE_SIZE 15
> +#define ARM64_WORKAROUND_CAVIUM_28168 16
>
> -#define ARM64_NCAPS 16
> +#define ARM64_NCAPS 17
>
> #endif /* __ASM_CPUCAPS_H */
> diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
> index b75e917..fac6d74 100644
> --- a/arch/arm64/kernel/cpu_errata.c
> +++ b/arch/arm64/kernel/cpu_errata.c
> @@ -123,6 +123,22 @@ static int cpu_enable_trap_ctr_access(void *__unused)
> MIDR_RANGE(MIDR_THUNDERX_81XX, 0x00, 0x00),
> },
> #endif
> +#ifdef CONFIG_CAVIUM_ERRATUM_28168
> + {
> + /* Cavium ThunderX, T88 pass 1.x - 2.1 */
> + .desc = "Cavium erratum 28168",
> + .capability = ARM64_WORKAROUND_CAVIUM_28168,
> + MIDR_RANGE(MIDR_THUNDERX, 0x00,
> + (1 << MIDR_VARIANT_SHIFT) | 1),
> + },
> + {
> + /* Cavium ThunderX, T81 pass 1.0 */
> + .desc = "Cavium erratum 28168",
> + .capability = ARM64_WORKAROUND_CAVIUM_28168,
> + MIDR_RANGE(MIDR_THUNDERX_81XX, 0x00, 0x00),
> + },
> +#endif
> +
> {
> .desc = "Mismatched cache line size",
> .capability = ARM64_MISMATCHED_CACHE_LINE_SIZE,
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
> index 06167da..451b393 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
> @@ -49,6 +49,8 @@
> #include <linux/spinlock.h>
>
> #include <linux/amba/bus.h>
> +#include <linux/arm-smmu.h>
> +
>
> #include "io-pgtable.h"
>
> @@ -577,6 +579,26 @@ static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
> }
> }
>
> +#ifdef CONFIG_CAVIUM_ERRATUM_28168
> +/*
> + * Cavium ThunderX erratum 28168
> + *
> + * Due to erratum #28168 PCI-inbound MSI-X store to the interrupt
> + * controller are delivered to the interrupt controller before older
> + * PCI-inbound memory stores are committed. Doing a sync on SMMU
> + * will make sure all prior data transfers are completed before
> + * invoking ISR.
> + *
> + */
> +void dev_smmu_tlb_sync(struct device *dev)
> +{
> + struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> + struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
> +
> + if (smmu)
> + __arm_smmu_tlb_sync(smmu);
> +}
> +#endif
I'll let Robin and Will comment on this, but it strikes be as rather odd
that nothing will happen if the SMMU is in bypass or simply compiled
out. So this workaround is at best incomplete.
> static void arm_smmu_tlb_sync(void *cookie)
> {
> struct arm_smmu_domain *smmu_domain = cookie;
> diff --git a/drivers/irqchip/irq-gic-v3-its-pci-msi.c b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
> index aee1c60..36ce696 100644
> --- a/drivers/irqchip/irq-gic-v3-its-pci-msi.c
> +++ b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
> @@ -20,6 +20,7 @@
> #include <linux/of.h>
> #include <linux/of_irq.h>
> #include <linux/of_pci.h>
> +#include <linux/arm-smmu.h>
>
> static void its_mask_msi_irq(struct irq_data *d)
> {
> @@ -86,11 +87,53 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
>
> /* ITS specific DeviceID, as the core ITS ignores dev. */
> info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
> -
> + info->scratchpad[1].ptr = &pdev->dev;
> return msi_info->ops->msi_prepare(domain->parent,
> dev, dev_alias.count, info);
> }
>
> +#ifdef CONFIG_CAVIUM_ERRATUM_28168
> +#define THUNDER_PME_DEVICE_ID 0xA100
> +
> +static void cavium_irq_preflow_handler(struct irq_data *data)
> +{
> + struct pci_dev *pdev;
> +
> + pdev = msi_desc_to_pci_dev(irq_data_get_msi_desc(data));
> + dev_smmu_tlb_sync(&pdev->dev);
> +}
Missing space between the two functions.
> +static void cavium_its_pci_msi_finish(msi_alloc_info_t *info, int ret)
> +{
> + struct device *dev = info->scratchpad[1].ptr;
> + struct pci_dev *pdev = to_pci_dev(dev);
> + struct device *parent_dev;
> + struct msi_desc *desc;
> +
> + if (ret)
> + return;
> +
> + if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) {
> + for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) {
> + pdev = to_pci_dev(parent_dev);
> + if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT)
> + continue;
> +
> + if (pdev->device == THUNDER_PME_DEVICE_ID)
> + for_each_pci_msi_entry(desc, to_pci_dev(dev))
> + __irq_set_preflow_handler(desc->irq, cavium_irq_preflow_handler);
So only the PME device is affected? Why can't you simply use INTx for it
(there is already some provision for that in the PME driver
("pcie_pme=nomsi").
If your PME device doesn't implement legacy interrupt, can't you do
something in the PME interrupt handler instead? Like reading from the
end-point or something similar?
> + break;
> + }
> + }
> +}
> +#else
> +static void cavium_irq_preflow_handler(struct irq_data *data)
> +{
> +}
Why is this defined? It is just going to generate a warning if
CONFIG_CAVIUM_ERRATUM_28168 is not defined...
> +static void cavium_its_pci_msi_finish(msi_alloc_info_t *info, int ret)
> +{
> +}
You might as well have
#define cavium_its_pci_msi_finish NULL
> +#endif
> +
> static struct msi_domain_ops its_pci_msi_ops = {
> .msi_prepare = its_pci_msi_prepare,
> };
> @@ -118,6 +161,9 @@ static int __init its_pci_msi_init_one(struct fwnode_handle *handle,
> return -ENXIO;
> }
>
> + if (cpus_have_cap(ARM64_WORKAROUND_CAVIUM_28168))
> + its_pci_msi_ops.msi_finish = cavium_its_pci_msi_finish;
> +
> if (!pci_msi_create_irq_domain(handle, &its_pci_msi_domain_info,
> parent)) {
> pr_err("%s: Unable to create PCI domain\n", name);
> diff --git a/include/linux/arm-smmu.h b/include/linux/arm-smmu.h
> new file mode 100644
> index 0000000..d3f5dff
> --- /dev/null
> +++ b/include/linux/arm-smmu.h
> @@ -0,0 +1,8 @@
> +#ifndef _ARM_SMMU_H
> +#define _ARM_SMMU_H
> +
> +#include<linux/pci.h>
> +
> +extern void dev_smmu_tlb_sync(struct device *dev);
> +
> +#endif /* _ARM_SMMU_H */
>
Thanks,
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* [PATCH v3] arm64: SMMU-v2: Workaround for Cavium ThunderX erratum 28168
From: Will Deacon @ 2016-12-20 11:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <bd950765-8c1f-14d5-2be8-efa76f2d6b6b@arm.com>
On Tue, Dec 20, 2016 at 11:52:58AM +0000, Marc Zyngier wrote:
> On 20/12/16 11:06, Geetha sowjanya wrote:
> > From: Tirumalesh Chalamarla <tchalamarla@caviumnetworks.com>
> > +#ifdef CONFIG_CAVIUM_ERRATUM_28168
> > +/*
> > + * Cavium ThunderX erratum 28168
> > + *
> > + * Due to erratum #28168 PCI-inbound MSI-X store to the interrupt
> > + * controller are delivered to the interrupt controller before older
> > + * PCI-inbound memory stores are committed. Doing a sync on SMMU
> > + * will make sure all prior data transfers are completed before
> > + * invoking ISR.
> > + *
> > + */
> > +void dev_smmu_tlb_sync(struct device *dev)
> > +{
> > + struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> > + struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
> > +
> > + if (smmu)
> > + __arm_smmu_tlb_sync(smmu);
> > +}
> > +#endif
>
> I'll let Robin and Will comment on this, but it strikes be as rather odd
> that nothing will happen if the SMMU is in bypass or simply compiled
> out. So this workaround is at best incomplete.
Agreed. Unless the SMMU is the cause of the issue, relying on it to
implement the workaround is fragile and unnecessarily introduces a linkage
between SMMU driver internals and the interrupt handling code.
Not a fan.
Will
^ permalink raw reply
* [PATCH 0/2] s5p-mfc fix for using reserved memory
From: Marek Szyprowski @ 2016-12-20 11:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481888915-19624-1-git-send-email-pankaj.dubey@samsung.com>
Hi Pankaj
On 2016-12-16 12:48, Pankaj Dubey wrote:
> It has been observed on ARM64 based Exynos SoC, if IOMMU is not enabled
> and we try to use reserved memory for MFC, reqbufs fails with below
> mentioned error
> ---------------------------------------------------------------------------
> V4L2 Codec decoding example application
> Kamil Debski <k.debski@samsung.com>
> Copyright 2012 Samsung Electronics Co., Ltd.
>
> Opening MFC.
> (mfc.c:mfc_open:58): MFC Info (/dev/video0): driver="s5p-mfc" \
> bus_info="platform:12c30000.mfc0" card="s5p-mfc-dec" fd=0x4[
> 42.339165] Remapping memory failed, error: -6
>
> MFC Open Success.
> (main.c:main:711): Successfully opened all necessary files and devices
> (mfc.c:mfc_dec_setup_output:103): Setup MFC decoding OUTPUT buffer \
> size=4194304 (requested=4194304)
> (mfc.c:mfc_dec_setup_output:120): Number of MFC OUTPUT buffers is 2 \
> (requested 2)
>
> [App] Out buf phy : 0x00000000, virt : 0xffffffff
> Output Length is = 0x300000
> Error (mfc.c:mfc_dec_setup_output:145): Failed to MMAP MFC OUTPUT buffer
> -------------------------------------------------------------------------
> This is because the device requesting for memory is mfc0.left not the parent mfc0.
> Hence setting of alloc_devs need to be done only if IOMMU is enabled
> and in that case both the left and right device is treated as mfc0 only.
> Also we need to populate vb2_queue's dev pointer with mfc dev pointer.
I also got this issue but Your solution is imho not the proper approach.
Too much hacking in the driver, while the issue is in the core. ARM64
requires
to call arch_setup_dma_ops() for each device that will be used for
dma-mapping.
So the issue is in drivers/of/of_reserved_mem.c - in
of_reserved_mem_device_init_by_idx() function, which should ensure that
arch_setup_dma_ops() is called also for the virtual devices for reserved
memory.
s5p-mfc driver however still requires some patching for
dma-mapping/iommu glue
used on ARM64 architecture.
> Smitha T Murthy (2):
> media: s5p-mfc: convert drivers to use the new vb2_queue dev field
> media: s5p-mfc: fix MMAP of mfc buffer during reqbufs
>
> drivers/media/platform/s5p-mfc/s5p_mfc.c | 2 ++
> drivers/media/platform/s5p-mfc/s5p_mfc_dec.c | 17 ++++++++++-------
> drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 18 +++++++++++-------
> 3 files changed, 23 insertions(+), 14 deletions(-)
>
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
^ permalink raw reply
* [PATCH] drm: zte: add overlay plane support
From: Shawn Guo @ 2016-12-20 12:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208155755.GY31595@intel.com>
Hi Ville,
On Thu, Dec 08, 2016 at 05:57:55PM +0200, Ville Syrj?l? wrote:
> On Thu, Dec 08, 2016 at 11:12:41AM +0800, Shawn Guo wrote:
> <snip>
> > +static void zx_vl_plane_atomic_update(struct drm_plane *plane,
> > + struct drm_plane_state *old_state)
> > +{
> > + struct zx_plane *zplane = to_zx_plane(plane);
> > + struct drm_framebuffer *fb = plane->state->fb;
> > + struct drm_gem_cma_object *cma_obj;
> > + void __iomem *layer = zplane->layer;
> > + void __iomem *hbsc = zplane->hbsc;
> > + void __iomem *paddr_reg;
> > + dma_addr_t paddr;
> > + u32 src_x, src_y, src_w, src_h;
> > + u32 dst_x, dst_y, dst_w, dst_h;
> > + uint32_t format;
> > + u32 fmt;
> > + int num_planes;
> > + int i;
> > +
> > + if (!fb)
> > + return;
> > +
> > + format = fb->pixel_format;
> > +
> > + src_x = plane->state->src_x >> 16;
> > + src_y = plane->state->src_y >> 16;
> > + src_w = plane->state->src_w >> 16;
> > + src_h = plane->state->src_h >> 16;
> > +
> > + dst_x = plane->state->crtc_x;
> > + dst_y = plane->state->crtc_y;
> > + dst_w = plane->state->crtc_w;
> > + dst_h = plane->state->crtc_h;
>
> This shouls use the clipped coordiantes.
Thanks for spotting this. I will fix it in v2.
Shawn
^ permalink raw reply
* [PATCH v2] drm: zte: add overlay plane support
From: Shawn Guo @ 2016-12-20 12:09 UTC (permalink / raw)
To: linux-arm-kernel
From: Shawn Guo <shawn.guo@linaro.org>
It enables VOU VL (Video Layer) to support overlay plane with scaling
function. VL0 has some quirks on scaling support. We chose to skip it
and only adds VL1 and VL2 into DRM core for now.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
Changes for v2:
- Use clipped coordinates for overlay position calculation
drivers/gpu/drm/zte/zx_plane.c | 293 ++++++++++++++++++++++++++++++++++--
drivers/gpu/drm/zte/zx_plane_regs.h | 51 +++++++
drivers/gpu/drm/zte/zx_vou.c | 149 +++++++++++++++++-
drivers/gpu/drm/zte/zx_vou.h | 3 +
drivers/gpu/drm/zte/zx_vou_regs.h | 18 +++
5 files changed, 497 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c
index 546eb92a94e8..8cd7cf71b2b0 100644
--- a/drivers/gpu/drm/zte/zx_plane.c
+++ b/drivers/gpu/drm/zte/zx_plane.c
@@ -40,6 +40,269 @@ struct zx_plane {
DRM_FORMAT_ARGB4444,
};
+static const uint32_t vl_formats[] = {
+ DRM_FORMAT_NV12, /* Semi-planar YUV420 */
+ DRM_FORMAT_YUV420, /* Planar YUV420 */
+ DRM_FORMAT_YUYV, /* Packed YUV422 */
+ DRM_FORMAT_YVYU,
+ DRM_FORMAT_UYVY,
+ DRM_FORMAT_VYUY,
+ DRM_FORMAT_YUV444, /* YUV444 8bit */
+ /*
+ * TODO: add formats below that HW supports:
+ * - YUV420 P010
+ * - YUV420 Hantro
+ * - YUV444 10bit
+ */
+};
+
+#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
+
+static int zx_vl_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *plane_state)
+{
+ struct drm_framebuffer *fb = plane_state->fb;
+ struct drm_crtc *crtc = plane_state->crtc;
+ struct drm_crtc_state *crtc_state;
+ struct drm_rect clip;
+ int min_scale = FRAC_16_16(1, 8);
+ int max_scale = FRAC_16_16(8, 1);
+
+ if (!crtc || !fb)
+ return 0;
+
+ crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state,
+ crtc);
+ if (WARN_ON(!crtc_state))
+ return -EINVAL;
+
+ /* nothing to check when disabling or disabled */
+ if (!crtc_state->enable)
+ return 0;
+
+ /* plane must be enabled */
+ if (!plane_state->crtc)
+ return -EINVAL;
+
+ clip.x1 = 0;
+ clip.y1 = 0;
+ clip.x2 = crtc_state->adjusted_mode.hdisplay;
+ clip.y2 = crtc_state->adjusted_mode.vdisplay;
+
+ return drm_plane_helper_check_state(plane_state, &clip,
+ min_scale, max_scale,
+ true, true);
+}
+
+static u32 zx_vl_get_fmt(uint32_t format)
+{
+ u32 val = 0;
+
+ switch (format) {
+ case DRM_FORMAT_NV12:
+ val = VL_FMT_YUV420;
+ break;
+ case DRM_FORMAT_YUV420:
+ val = VL_YUV420_PLANAR | VL_FMT_YUV420;
+ break;
+ case DRM_FORMAT_YUYV:
+ val = VL_YUV422_YUYV | VL_FMT_YUV422;
+ break;
+ case DRM_FORMAT_YVYU:
+ val = VL_YUV422_YVYU | VL_FMT_YUV422;
+ break;
+ case DRM_FORMAT_UYVY:
+ val = VL_YUV422_UYVY | VL_FMT_YUV422;
+ break;
+ case DRM_FORMAT_VYUY:
+ val = VL_YUV422_VYUY | VL_FMT_YUV422;
+ break;
+ case DRM_FORMAT_YUV444:
+ val = VL_FMT_YUV444_8BIT;
+ break;
+ default:
+ WARN_ONCE(1, "invalid pixel format %d\n", format);
+ }
+
+ return val;
+}
+
+static inline void zx_vl_set_update(struct zx_plane *zplane)
+{
+ void __iomem *layer = zplane->layer;
+
+ zx_writel_mask(layer + VL_CTRL0, VL_UPDATE, VL_UPDATE);
+}
+
+static inline void zx_vl_rsz_set_update(struct zx_plane *zplane)
+{
+ zx_writel(zplane->rsz + RSZ_VL_ENABLE_CFG, 1);
+}
+
+static u32 zx_vl_rsz_get_fmt(uint32_t format)
+{
+ u32 val = 0;
+
+ switch (format) {
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_YUV420:
+ val = RSZ_VL_FMT_YCBCR420;
+ break;
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ val = RSZ_VL_FMT_YCBCR422;
+ break;
+ case DRM_FORMAT_YUV444:
+ val = RSZ_VL_FMT_YCBCR444;
+ break;
+ default:
+ WARN_ONCE(1, "invalid pixel format %d\n", format);
+ }
+
+ return val;
+}
+
+static inline u32 rsz_step_value(u32 src, u32 dst)
+{
+ u32 val = 0;
+
+ if (src == dst)
+ val = 0;
+ else if (src < dst)
+ val = RSZ_PARA_STEP((src << 16) / dst);
+ else if (src > dst)
+ val = RSZ_DATA_STEP(src / dst) |
+ RSZ_PARA_STEP(((src << 16) / dst) & 0xffff);
+
+ return val;
+}
+
+static void zx_vl_rsz_setup(struct zx_plane *zplane, uint32_t format,
+ u32 src_w, u32 src_h, u32 dst_w, u32 dst_h)
+{
+ void __iomem *rsz = zplane->rsz;
+ u32 src_chroma_w = src_w;
+ u32 src_chroma_h = src_h;
+ u32 fmt;
+
+ /* Set up source and destination resolution */
+ zx_writel(rsz + RSZ_SRC_CFG, RSZ_VER(src_h - 1) | RSZ_HOR(src_w - 1));
+ zx_writel(rsz + RSZ_DEST_CFG, RSZ_VER(dst_h - 1) | RSZ_HOR(dst_w - 1));
+
+ /* Configure data format for VL RSZ */
+ fmt = zx_vl_rsz_get_fmt(format);
+ zx_writel_mask(rsz + RSZ_VL_CTRL_CFG, RSZ_VL_FMT_MASK, fmt);
+
+ /* Calculate Chroma heigth and width */
+ if (fmt == RSZ_VL_FMT_YCBCR420) {
+ src_chroma_w = src_w >> 1;
+ src_chroma_h = src_h >> 1;
+ } else if (fmt == RSZ_VL_FMT_YCBCR422) {
+ src_chroma_w = src_w >> 1;
+ }
+
+ /* Set up Luma and Chroma step registers */
+ zx_writel(rsz + RSZ_VL_LUMA_HOR, rsz_step_value(src_w, dst_w));
+ zx_writel(rsz + RSZ_VL_LUMA_VER, rsz_step_value(src_h, dst_h));
+ zx_writel(rsz + RSZ_VL_CHROMA_HOR, rsz_step_value(src_chroma_w, dst_w));
+ zx_writel(rsz + RSZ_VL_CHROMA_VER, rsz_step_value(src_chroma_h, dst_h));
+
+ zx_vl_rsz_set_update(zplane);
+}
+
+static void zx_vl_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct zx_plane *zplane = to_zx_plane(plane);
+ struct drm_plane_state *state = plane->state;
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_rect *src = &state->src;
+ struct drm_rect *dst = &state->dst;
+ struct drm_gem_cma_object *cma_obj;
+ void __iomem *layer = zplane->layer;
+ void __iomem *hbsc = zplane->hbsc;
+ void __iomem *paddr_reg;
+ dma_addr_t paddr;
+ u32 src_x, src_y, src_w, src_h;
+ u32 dst_x, dst_y, dst_w, dst_h;
+ uint32_t format;
+ u32 fmt;
+ int num_planes;
+ int i;
+
+ if (!fb)
+ return;
+
+ format = fb->pixel_format;
+
+ src_x = src->x1 >> 16;
+ src_y = src->y1 >> 16;
+ src_w = drm_rect_width(src) >> 16;
+ src_h = drm_rect_height(src) >> 16;
+
+ dst_x = dst->x1;
+ dst_y = dst->y1;
+ dst_w = drm_rect_width(dst);
+ dst_h = drm_rect_height(dst);
+
+ /* Set up data address registers for Y, Cb and Cr planes */
+ num_planes = drm_format_num_planes(format);
+ paddr_reg = layer + VL_Y;
+ for (i = 0; i < num_planes; i++) {
+ cma_obj = drm_fb_cma_get_gem_obj(fb, i);
+ paddr = cma_obj->paddr + fb->offsets[i];
+ paddr += src_y * fb->pitches[i];
+ paddr += src_x * drm_format_plane_cpp(format, i);
+ zx_writel(paddr_reg, paddr);
+ paddr_reg += 4;
+ }
+
+ /* Set up source height/width register */
+ zx_writel(layer + VL_SRC_SIZE, GL_SRC_W(src_w) | GL_SRC_H(src_h));
+
+ /* Set up start position register */
+ zx_writel(layer + VL_POS_START, GL_POS_X(dst_x) | GL_POS_Y(dst_y));
+
+ /* Set up end position register */
+ zx_writel(layer + VL_POS_END,
+ GL_POS_X(dst_x + dst_w) | GL_POS_Y(dst_y + dst_h));
+
+ /* Strides of Cb and Cr planes should be identical */
+ zx_writel(layer + VL_STRIDE, LUMA_STRIDE(fb->pitches[0]) |
+ CHROMA_STRIDE(fb->pitches[1]));
+
+ /* Set up video layer data format */
+ fmt = zx_vl_get_fmt(format);
+ zx_writel(layer + VL_CTRL1, fmt);
+
+ /* Always use scaler since it exists (set for not bypass) */
+ zx_writel_mask(layer + VL_CTRL2, VL_SCALER_BYPASS_MODE,
+ VL_SCALER_BYPASS_MODE);
+
+ zx_vl_rsz_setup(zplane, format, src_w, src_h, dst_w, dst_h);
+
+ /* Enable HBSC block */
+ zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN);
+
+ zx_overlay_enable(plane);
+
+ zx_vl_set_update(zplane);
+}
+
+static void zx_vl_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ zx_overlay_disable(plane);
+}
+
+static const struct drm_plane_helper_funcs zx_vl_plane_helper_funcs = {
+ .atomic_check = zx_vl_plane_atomic_check,
+ .atomic_update = zx_vl_plane_atomic_update,
+ .atomic_disable = zx_vl_plane_atomic_disable,
+};
+
static int zx_gl_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *plane_state)
{
@@ -107,14 +370,6 @@ static inline void zx_gl_rsz_set_update(struct zx_plane *zplane)
zx_writel(zplane->rsz + RSZ_ENABLE_CFG, 1);
}
-void zx_plane_set_update(struct drm_plane *plane)
-{
- struct zx_plane *zplane = to_zx_plane(plane);
-
- zx_gl_rsz_set_update(zplane);
- zx_gl_set_update(zplane);
-}
-
static void zx_gl_rsz_setup(struct zx_plane *zplane, u32 src_w, u32 src_h,
u32 dst_w, u32 dst_h)
{
@@ -230,6 +485,24 @@ static void zx_plane_destroy(struct drm_plane *plane)
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
+void zx_plane_set_update(struct drm_plane *plane)
+{
+ struct zx_plane *zplane = to_zx_plane(plane);
+
+ switch (plane->type) {
+ case DRM_PLANE_TYPE_PRIMARY:
+ zx_gl_rsz_set_update(zplane);
+ zx_gl_set_update(zplane);
+ break;
+ case DRM_PLANE_TYPE_OVERLAY:
+ zx_vl_rsz_set_update(zplane);
+ zx_vl_set_update(zplane);
+ break;
+ default:
+ WARN_ONCE(1, "unsupported plane type %d\n", plane->type);
+ }
+}
+
static void zx_plane_hbsc_init(struct zx_plane *zplane)
{
void __iomem *hbsc = zplane->hbsc;
@@ -279,7 +552,9 @@ struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
format_count = ARRAY_SIZE(gl_formats);
break;
case DRM_PLANE_TYPE_OVERLAY:
- /* TODO: add video layer (vl) support */
+ helper = &zx_vl_plane_helper_funcs;
+ formats = vl_formats;
+ format_count = ARRAY_SIZE(vl_formats);
break;
default:
return ERR_PTR(-ENODEV);
diff --git a/drivers/gpu/drm/zte/zx_plane_regs.h b/drivers/gpu/drm/zte/zx_plane_regs.h
index 3dde6716a558..65f271aeabed 100644
--- a/drivers/gpu/drm/zte/zx_plane_regs.h
+++ b/drivers/gpu/drm/zte/zx_plane_regs.h
@@ -46,6 +46,37 @@
#define GL_POS_X(x) (((x) << GL_POS_X_SHIFT) & GL_POS_X_MASK)
#define GL_POS_Y(x) (((x) << GL_POS_Y_SHIFT) & GL_POS_Y_MASK)
+/* VL registers */
+#define VL_CTRL0 0x00
+#define VL_UPDATE BIT(3)
+#define VL_CTRL1 0x04
+#define VL_YUV420_PLANAR BIT(5)
+#define VL_YUV422_SHIFT 3
+#define VL_YUV422_YUYV (0 << VL_YUV422_SHIFT)
+#define VL_YUV422_YVYU (1 << VL_YUV422_SHIFT)
+#define VL_YUV422_UYVY (2 << VL_YUV422_SHIFT)
+#define VL_YUV422_VYUY (3 << VL_YUV422_SHIFT)
+#define VL_FMT_YUV420 0
+#define VL_FMT_YUV422 1
+#define VL_FMT_YUV420_P010 2
+#define VL_FMT_YUV420_HANTRO 3
+#define VL_FMT_YUV444_8BIT 4
+#define VL_FMT_YUV444_10BIT 5
+#define VL_CTRL2 0x08
+#define VL_SCALER_BYPASS_MODE BIT(0)
+#define VL_STRIDE 0x0c
+#define LUMA_STRIDE_SHIFT 16
+#define LUMA_STRIDE_MASK (0xffff << LUMA_STRIDE_SHIFT)
+#define CHROMA_STRIDE_SHIFT 0
+#define CHROMA_STRIDE_MASK (0xffff << CHROMA_STRIDE_SHIFT)
+#define VL_SRC_SIZE 0x10
+#define VL_Y 0x14
+#define VL_POS_START 0x30
+#define VL_POS_END 0x34
+
+#define LUMA_STRIDE(x) (((x) << LUMA_STRIDE_SHIFT) & LUMA_STRIDE_MASK)
+#define CHROMA_STRIDE(x) (((x) << CHROMA_STRIDE_SHIFT) & CHROMA_STRIDE_MASK)
+
/* CSC registers */
#define CSC_CTRL0 0x30
#define CSC_COV_MODE_SHIFT 16
@@ -69,6 +100,18 @@
#define RSZ_DEST_CFG 0x04
#define RSZ_ENABLE_CFG 0x14
+#define RSZ_VL_LUMA_HOR 0x08
+#define RSZ_VL_LUMA_VER 0x0c
+#define RSZ_VL_CHROMA_HOR 0x10
+#define RSZ_VL_CHROMA_VER 0x14
+#define RSZ_VL_CTRL_CFG 0x18
+#define RSZ_VL_FMT_SHIFT 3
+#define RSZ_VL_FMT_MASK (0x3 << RSZ_VL_FMT_SHIFT)
+#define RSZ_VL_FMT_YCBCR420 (0x0 << RSZ_VL_FMT_SHIFT)
+#define RSZ_VL_FMT_YCBCR422 (0x1 << RSZ_VL_FMT_SHIFT)
+#define RSZ_VL_FMT_YCBCR444 (0x2 << RSZ_VL_FMT_SHIFT)
+#define RSZ_VL_ENABLE_CFG 0x1c
+
#define RSZ_VER_SHIFT 16
#define RSZ_VER_MASK (0xffff << RSZ_VER_SHIFT)
#define RSZ_HOR_SHIFT 0
@@ -77,6 +120,14 @@
#define RSZ_VER(x) (((x) << RSZ_VER_SHIFT) & RSZ_VER_MASK)
#define RSZ_HOR(x) (((x) << RSZ_HOR_SHIFT) & RSZ_HOR_MASK)
+#define RSZ_DATA_STEP_SHIFT 16
+#define RSZ_DATA_STEP_MASK (0xffff << RSZ_DATA_STEP_SHIFT)
+#define RSZ_PARA_STEP_SHIFT 0
+#define RSZ_PARA_STEP_MASK (0xffff << RSZ_PARA_STEP_SHIFT)
+
+#define RSZ_DATA_STEP(x) (((x) << RSZ_DATA_STEP_SHIFT) & RSZ_DATA_STEP_MASK)
+#define RSZ_PARA_STEP(x) (((x) << RSZ_PARA_STEP_SHIFT) & RSZ_PARA_STEP_MASK)
+
/* HBSC registers */
#define HBSC_SATURATION 0x00
#define HBSC_HUE 0x04
diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
index 73fe15c17c32..8ca9c4bdeeaf 100644
--- a/drivers/gpu/drm/zte/zx_vou.c
+++ b/drivers/gpu/drm/zte/zx_vou.c
@@ -93,10 +93,38 @@ struct zx_crtc {
const struct zx_crtc_bits *bits;
enum vou_chn_type chn_type;
struct clk *pixclk;
+ u32 overlay_bitmap;
};
#define to_zx_crtc(x) container_of(x, struct zx_crtc, crtc)
+struct zx_vl_bits {
+ u32 enable;
+ u32 chnsel;
+ u32 clksel;
+};
+
+static const struct zx_vl_bits zx_vl_bits[VL_NUM] = {
+ {
+ .enable = OSD_CTRL0_VL0_EN,
+ .chnsel = OSD_CTRL0_VL0_SEL,
+ .clksel = VOU_CLK_VL0_SEL,
+ }, {
+ .enable = OSD_CTRL0_VL1_EN,
+ .chnsel = OSD_CTRL0_VL1_SEL,
+ .clksel = VOU_CLK_VL1_SEL,
+ }, {
+ .enable = OSD_CTRL0_VL2_EN,
+ .chnsel = OSD_CTRL0_VL2_SEL,
+ .clksel = VOU_CLK_VL2_SEL,
+ },
+};
+
+struct zx_overlay {
+ struct drm_plane *plane;
+ const struct zx_vl_bits *bits;
+};
+
struct zx_vou_hw {
struct device *dev;
void __iomem *osd;
@@ -110,6 +138,7 @@ struct zx_vou_hw {
struct clk *aux_clk;
struct zx_crtc *main_crtc;
struct zx_crtc *aux_crtc;
+ struct zx_overlay overlays[VL_NUM];
};
static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
@@ -404,6 +433,112 @@ void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe)
zcrtc->bits->int_frame_mask, 0);
}
+static int zx_overlay_find_vl_idx(struct drm_plane *plane,
+ struct zx_vou_hw *vou)
+{
+ int i;
+
+ for (i = 0; i < VL_NUM; i++) {
+ if (vou->overlays[i].plane == plane)
+ break;
+ }
+
+ if (i == VL_NUM) {
+ DRM_DEV_ERROR(vou->dev, "failed to find VL\n");
+ return -EINVAL;
+ }
+
+ return i;
+}
+
+void zx_overlay_enable(struct drm_plane *plane)
+{
+ struct zx_crtc *zcrtc = to_zx_crtc(plane->state->crtc);
+ struct zx_vou_hw *vou = zcrtc->vou;
+ const struct zx_vl_bits *bits;
+ int idx;
+
+ idx = zx_overlay_find_vl_idx(plane, vou);
+ if (idx < 0)
+ return;
+
+ bits = vou->overlays[idx].bits;
+ zcrtc->overlay_bitmap |= 1 << idx;
+
+ if (zcrtc->chn_type == VOU_CHN_MAIN) {
+ zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel, 0);
+ zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel, 0);
+ } else {
+ zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel,
+ bits->chnsel);
+ zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel,
+ bits->clksel);
+ }
+
+ zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, bits->enable);
+}
+
+void zx_overlay_disable(struct drm_plane *plane)
+{
+ struct zx_crtc *zcrtc = to_zx_crtc(plane->crtc);
+ struct zx_vou_hw *vou = zcrtc->vou;
+ const struct zx_vl_bits *bits;
+ int idx;
+
+ idx = zx_overlay_find_vl_idx(plane, vou);
+ if (idx < 0)
+ return;
+
+ bits = vou->overlays[idx].bits;
+ zcrtc->overlay_bitmap &= ~(1 << idx);
+
+ zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, 0);
+}
+
+static void zx_overlay_init(struct drm_device *drm, struct zx_vou_hw *vou)
+{
+ struct device *dev = vou->dev;
+ struct drm_plane *plane;
+ struct zx_layer_data data;
+ int i;
+
+ /*
+ * VL0 has some quirks on scaling support which need special handling.
+ * Let's leave it out for now.
+ */
+ for (i = 1; i < VL_NUM; i++) {
+ data.layer = vou->osd + OSD_VL_OFFSET(i);
+ data.hbsc = vou->osd + HBSC_VL_OFFSET(i);
+ data.rsz = vou->otfppu + RSZ_VL_OFFSET(i);
+
+ plane = zx_plane_init(drm, dev, &data, DRM_PLANE_TYPE_OVERLAY);
+ if (IS_ERR(plane)) {
+ DRM_DEV_ERROR(dev, "failed to init overlay %d\n", i);
+ continue;
+ }
+
+ vou->overlays[i].plane = plane;
+ vou->overlays[i].bits = &zx_vl_bits[i];
+ }
+}
+
+static inline void zx_osd_int_update(struct zx_crtc *zcrtc)
+{
+ u32 bitmap = zcrtc->overlay_bitmap;
+ int i;
+
+ vou_chn_set_update(zcrtc);
+ zx_plane_set_update(zcrtc->primary);
+
+ if (bitmap) {
+ for (i = 0; i < VL_NUM; i++) {
+ if ((bitmap & (1 << i)) == 0)
+ continue;
+ zx_plane_set_update(zcrtc->vou->overlays[i].plane);
+ }
+ }
+}
+
static irqreturn_t vou_irq_handler(int irq, void *dev_id)
{
struct zx_vou_hw *vou = dev_id;
@@ -423,15 +558,11 @@ static irqreturn_t vou_irq_handler(int irq, void *dev_id)
state = zx_readl(vou->osd + OSD_INT_STA);
zx_writel(vou->osd + OSD_INT_CLRSTA, state);
- if (state & OSD_INT_MAIN_UPT) {
- vou_chn_set_update(vou->main_crtc);
- zx_plane_set_update(vou->main_crtc->primary);
- }
+ if (state & OSD_INT_MAIN_UPT)
+ zx_osd_int_update(vou->main_crtc);
- if (state & OSD_INT_AUX_UPT) {
- vou_chn_set_update(vou->aux_crtc);
- zx_plane_set_update(vou->aux_crtc->primary);
- }
+ if (state & OSD_INT_AUX_UPT)
+ zx_osd_int_update(vou->aux_crtc);
if (state & OSD_INT_ERROR)
DRM_DEV_ERROR(vou->dev, "OSD ERROR: 0x%08x!\n", state);
@@ -611,6 +742,8 @@ static int zx_crtc_bind(struct device *dev, struct device *master, void *data)
goto disable_ppu_clk;
}
+ zx_overlay_init(drm, vou);
+
return 0;
disable_ppu_clk:
diff --git a/drivers/gpu/drm/zte/zx_vou.h b/drivers/gpu/drm/zte/zx_vou.h
index 349e06cd86f4..1559c1f79db7 100644
--- a/drivers/gpu/drm/zte/zx_vou.h
+++ b/drivers/gpu/drm/zte/zx_vou.h
@@ -43,4 +43,7 @@ struct vou_inf {
int zx_vou_enable_vblank(struct drm_device *drm, unsigned int pipe);
void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe);
+void zx_overlay_enable(struct drm_plane *plane);
+void zx_overlay_disable(struct drm_plane *plane);
+
#endif /* __ZX_VOU_H__ */
diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h
index f44e7a4ae441..193c1ce01fe7 100644
--- a/drivers/gpu/drm/zte/zx_vou_regs.h
+++ b/drivers/gpu/drm/zte/zx_vou_regs.h
@@ -22,6 +22,15 @@
#define AUX_HBSC_OFFSET 0x860
#define AUX_RSZ_OFFSET 0x800
+#define OSD_VL0_OFFSET 0x040
+#define OSD_VL_OFFSET(i) (OSD_VL0_OFFSET + 0x050 * (i))
+
+#define HBSC_VL0_OFFSET 0x760
+#define HBSC_VL_OFFSET(i) (HBSC_VL0_OFFSET + 0x040 * (i))
+
+#define RSZ_VL1_U0 0xa00
+#define RSZ_VL_OFFSET(i) (RSZ_VL1_U0 + 0x200 * (i))
+
/* OSD (GPC_GLOBAL) registers */
#define OSD_INT_STA 0x04
#define OSD_INT_CLRSTA 0x08
@@ -42,6 +51,12 @@
)
#define OSD_INT_ENABLE (OSD_INT_ERROR | OSD_INT_AUX_UPT | OSD_INT_MAIN_UPT)
#define OSD_CTRL0 0x10
+#define OSD_CTRL0_VL0_EN BIT(13)
+#define OSD_CTRL0_VL0_SEL BIT(12)
+#define OSD_CTRL0_VL1_EN BIT(11)
+#define OSD_CTRL0_VL1_SEL BIT(10)
+#define OSD_CTRL0_VL2_EN BIT(9)
+#define OSD_CTRL0_VL2_SEL BIT(8)
#define OSD_CTRL0_GL0_EN BIT(7)
#define OSD_CTRL0_GL0_SEL BIT(6)
#define OSD_CTRL0_GL1_EN BIT(5)
@@ -146,6 +161,9 @@
#define VOU_INF_DATA_SEL 0x08
#define VOU_SOFT_RST 0x14
#define VOU_CLK_SEL 0x18
+#define VOU_CLK_VL2_SEL BIT(8)
+#define VOU_CLK_VL1_SEL BIT(7)
+#define VOU_CLK_VL0_SEL BIT(6)
#define VOU_CLK_GL1_SEL BIT(5)
#define VOU_CLK_GL0_SEL BIT(4)
#define VOU_CLK_REQEN 0x20
--
1.9.1
^ permalink raw reply related
* [PATCH] crypto: testmgr: Use linear alias for test input
From: Ard Biesheuvel @ 2016-12-20 12:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482190646-7720-1-git-send-email-labbott@redhat.com>
On 19 December 2016 at 23:37, Laura Abbott <labbott@redhat.com> wrote:
> Christopher Covington reported a crash on aarch64 on recent Fedora
> kernels:
>
> kernel BUG at ./include/linux/scatterlist.h:140!
> Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
> Modules linked in:
> CPU: 2 PID: 752 Comm: cryptomgr_test Not tainted 4.9.0-11815-ge93b1cc #162
> Hardware name: linux,dummy-virt (DT)
> task: ffff80007c650080 task.stack: ffff800008910000
> PC is at sg_init_one+0xa0/0xb8
> LR is at sg_init_one+0x24/0xb8
> ...
> [<ffff000008398db8>] sg_init_one+0xa0/0xb8
> [<ffff000008350a44>] test_acomp+0x10c/0x438
> [<ffff000008350e20>] alg_test_comp+0xb0/0x118
> [<ffff00000834f28c>] alg_test+0x17c/0x2f0
> [<ffff00000834c6a4>] cryptomgr_test+0x44/0x50
> [<ffff0000080dac70>] kthread+0xf8/0x128
> [<ffff000008082ec0>] ret_from_fork+0x10/0x50
>
> The test vectors used for input are part of the kernel image. These
> inputs are passed as a buffer to sg_init_one which eventually blows up
> with BUG_ON(!virt_addr_valid(buf)). On arm64, virt_addr_valid returns
> false for the kernel image since virt_to_page will not return the
> correct page. The kernel image is also aliased to the linear map so get
> the linear alias and pass that to the scatterlist instead.
>
> Reported-by: Christopher Covington <cov@codeaurora.org>
> Fixes: d7db7a882deb ("crypto: acomp - update testmgr with support for acomp")
> Signed-off-by: Laura Abbott <labbott@redhat.com>
> ---
> x86 supports virt_addr_valid working on kernel image addresses but arm64 is
> more strict. This is the direction things have been moving with my
> CONFIG_DEBUG_VIRTUAL series for arm64 which is tightening the definition of
> __pa/__pa_symbol.
A helper function would be nice, so that we can call
sg_init_table/sg_set_page directly, and avoid the redundant
virt_to_phys(__va()) translation (and add a comment *why* we should
not use sg_init_one() with the address of a kernel symbol).
But I will leave it up to Herbert to decide whether he prefers that or not.
In any case,
Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
> crypto/testmgr.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/crypto/testmgr.c b/crypto/testmgr.c
> index f616ad7..f5bac10 100644
> --- a/crypto/testmgr.c
> +++ b/crypto/testmgr.c
> @@ -1464,7 +1464,7 @@ static int test_acomp(struct crypto_acomp *tfm, struct comp_testvec *ctemplate,
>
> memset(output, 0, dlen);
> init_completion(&result.completion);
> - sg_init_one(&src, ctemplate[i].input, ilen);
> + sg_init_one(&src, __va(__pa_symbol(ctemplate[i].input)), ilen);
> sg_init_one(&dst, output, dlen);
>
> req = acomp_request_alloc(tfm);
> @@ -1513,7 +1513,7 @@ static int test_acomp(struct crypto_acomp *tfm, struct comp_testvec *ctemplate,
>
> memset(output, 0, dlen);
> init_completion(&result.completion);
> - sg_init_one(&src, dtemplate[i].input, ilen);
> + sg_init_one(&src, __va(__pa_symbol(dtemplate[i].input)), ilen);
> sg_init_one(&dst, output, dlen);
>
> req = acomp_request_alloc(tfm);
> --
> 2.7.4
>
^ permalink raw reply
* [PATCH] ARM: dts: sun8i-q8-common: enable bluetooth on SDIO Wi-Fi
From: Maxime Ripard @ 2016-12-20 13:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAGb2v67wU+Hw-6oa9FxzB33sZY8Up-59HZ_mneFBdcRhXbU1pA@mail.gmail.com>
On Mon, Dec 19, 2016 at 10:24:44PM +0800, Chen-Yu Tsai wrote:
> On Mon, Dec 19, 2016 at 10:08 PM, Icenowy Zheng <icenowy@aosc.xyz> wrote:
> >
> >
> > 19.12.2016, 18:09, "Maxime Ripard" <maxime.ripard@free-electrons.com>:
> >> On Fri, Dec 16, 2016 at 10:40:00PM +0800, Icenowy Zheng wrote:
> >>> >> > > &r_pio {
> >>> >> > > wifi_pwrseq_pin_q8: wifi_pwrseq_pin at 0 {
> >>> >> > > - pins = "PL6", "PL7", "PL11";
> >>> >> > > + pins = "PL6", "PL7", "PL8", "PL11";
> >>> >> > > function = "gpio_in";
> >>> >> > > bias-pull-up;
> >>> >> > > };
> >>> >> >
> >>> >> > There's several things wrong here. The first one is that you rely
> >>> >> > solely on the pinctrl state to maintain a reset line. This is very
> >>> >> > fragile (especially since the GPIO pinctrl state are likely to go away
> >>> >> > at some point), but it also means that if your driver wants to recover
> >>> >> > from that situation at some point, it won't work.
> >>> >> >
> >>> >> > The other one is that the bluetooth and wifi chips are two devices in
> >>> >> > linux, and you assign that pin to the wrong device (wifi).
> >>> >> >
> >>> >> > rfkill-gpio is made just for that, so please use it.
> >>> >>
> >>> >> The GPIO is not for the radio, but for the full Bluetooth part.
> >>> >
> >>> > I know.
> >>> >
> >>> >> If it's set to 0, then the bluetooth part will reset, and the
> >>> >> hciattach will fail.
> >>> >
> >>> > Both rfkill-gpio and rfkill-regulator will shutdown when called
> >>> > (either by poking the reset pin or shutting down the regulator), so
> >>> > that definitely seems like an expected behavior to put the device in
> >>> > reset.
> >>> >
> >>> >> The BSP uses this as a rfkill, and the result is that the bluetooth
> >>> >> on/off switch do not work properly.
> >>> >
> >>> > Then rfkill needs fixing, but working around it by hoping that the
> >>> > core will probe an entirely different device, and enforcing a default
> >>> > that the rest of the kernel might or might not change is both fragile
> >>> > and wrong.
> >>>
> >>> I think a rfkill-gpio here works just like the BSP rfkill...
> >>>
> >>> The real problem is that the Realtek UART bluetooth driver is a userspace
> >>> program (a modified hciattach), which is not capable of the GPIO reset...
> >>
> >> Can't you run rfkill before attaching? What is the problem exactly?
> >> It's not in reset for long enough?
> >>
> >> This seems more and more like an issue in the BT stack you're
> >> using. We might consider workarounds in the kernel, but they have to
> >> be correct.
> >
> > One more rfkill interface will be generated for hci0 after hciattach, which can
> > be safely toggled block and unblock.
> >
> > However, if the GPIO is toggled down, the hciattach program will die.
> >
> > The bluetooth stack I used is fd.o's BlueZ.
>
> I think the bigger issue is that the tty/serial subsystem does not have
> power sequencing support. Here we're trying to use rfkill to do that,
> but that doesn't seem to be what it was intended for. It might work with
> standalone USB bluetooth controllers that don't need any special setup,
> since the device will just appear and get registered. But it might not
> work so well with UART based adapters that need userspace fiddling with
> firmware and hciattach.
Then you can also have a look at the generic power sequence patches
that are floating around.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161220/4ffe5077/attachment.sig>
^ permalink raw reply
* [linux-sunxi][PATCH 2/3] ARM: dts: sun6i: Add the SPDIF block to the A31
From: Maxime Ripard @ 2016-12-20 14:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161220104038.22532-3-codekipper@gmail.com>
Hi,
On Tue, Dec 20, 2016 at 11:40:37AM +0100, codekipper at gmail.com wrote:
> From: Marcus Cooper <codekipper@gmail.com>
>
> Add the SPDIF transceiver controller block to the A31 dtsi.
>
> Signed-off-by: Marcus Cooper <codekipper@gmail.com>
> ---
> arch/arm/boot/dts/sun6i-a31.dtsi | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
> index 7370ba6c9993..559c53efa7e6 100644
> --- a/arch/arm/boot/dts/sun6i-a31.dtsi
> +++ b/arch/arm/boot/dts/sun6i-a31.dtsi
> @@ -613,6 +613,20 @@
> reg = <0x01c20ca0 0x20>;
> };
>
> + spdif: spdif at 01c21000 {
> + #sound-dai-cells = <0>;
> + compatible = "allwinner,sun6i-a31-spdif";
> + reg = <0x01c21000 0x400>;
> + interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&ccu CLK_APB1_SPDIF>, <&ccu CLK_SPDIF>;
> + resets = <&ccu RST_APB1_SPDIF>;
> + clock-names = "apb", "spdif";
> + dmas = <&dma 2>, <&dma 2>;
> + dma-names = "rx", "tx";
> + spdif-out = "disabled";
That property isn't documented anywhere, and doesn't seem to be used
in your driver either.
On a separate topic, is the channel inversion bug also found on the
A31?
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161220/b0c1450a/attachment.sig>
^ permalink raw reply
* [GIT PULL] Mailbox changes for v4.10
From: Jassi Brar @ 2016-12-20 14:14 UTC (permalink / raw)
To: linux-arm-kernel
Hi Linus,
The following changes since commit 16ae16c6e5616c084168740990fc508bda6655d4:
Merge tag 'mmc-v4.9-rc5' of
git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc (2016-11-24
10:51:18 -0800)
are available in the git repository at:
git://git.linaro.org/landing-teams/working/fujitsu/integration.git
mailbox-for-next
for you to fetch changes up to db4d22c07e3e652eeec82abcc1399e6305edd838:
mailbox: mailbox-test: allow reserved areas in SRAM (2016-12-19
20:10:23 +0530)
----------------------------------------------------------------
- New features (poll and SRAM usage) added to the mailbox-test driver
- Major update of Broadcom's PDC controller driver
- Minor fix for auto-loading test and STI driver modules
----------------------------------------------------------------
Javier Martinez Canillas (2):
mailbox: mailbox-test: Fix module autoload
mailbox: sti: Fix module autoload for OF registration
Rob Rice (9):
mailbox: bcm-pdc: Use octal permissions rather than symbolic
mailbox: bcm-pdc: Convert from interrupts to poll for tx done
mailbox: bcm-pdc: streamline rx code
mailbox: bcm-pdc: Try to improve branch prediction
mailbox: bcm-pdc: Convert from threaded IRQ to tasklet
mailbox: bcm-pdc: Don't use iowrite32 to write DMA descriptors
mailbox: bcm-pdc: Performance improvements
mailbox: bcm-pdc: Simplify interrupt handler logic
mailbox: bcm-pdc: Remove unnecessary void* casts
Steve Lin (2):
mailbox: bcm-pdc: Changes so mbox client can be removed / re-inserted
mailbox: bcm-pdc: PDC driver leaves debugfs files after removal
Sudeep Holla (2):
mailbox: mailbox-test: add support for fasync/poll
mailbox: mailbox-test: allow reserved areas in SRAM
drivers/mailbox/bcm-pdc-mailbox.c | 548 ++++++++++++++++++++++----------------
drivers/mailbox/mailbox-sti.c | 1 +
drivers/mailbox/mailbox-test.c | 92 ++++++-
3 files changed, 407 insertions(+), 234 deletions(-)
^ permalink raw reply
* [linux-sunxi][PATCH] ARM: dts: sun4i: A1000: add axp209 regulator nodes
From: Maxime Ripard @ 2016-12-20 14:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161220102242.2423-1-codekipper@gmail.com>
On Tue, Dec 20, 2016 at 11:22:42AM +0100, codekipper at gmail.com wrote:
> From: Marcus Cooper <codekipper@gmail.com>
>
> This patch adds the regulator nodes for the axp209 by including
> the axp209 dtsi.
>
> Signed-off-by: Marcus Cooper <codekipper@gmail.com>
> ---
> arch/arm/boot/dts/sun4i-a10-a1000.dts | 34 ++++++++++++++++++++++++++++++++++
> 1 file changed, 34 insertions(+)
>
> diff --git a/arch/arm/boot/dts/sun4i-a10-a1000.dts b/arch/arm/boot/dts/sun4i-a10-a1000.dts
> index 68c6bdb2cf7c..e7394d701856 100644
> --- a/arch/arm/boot/dts/sun4i-a10-a1000.dts
> +++ b/arch/arm/boot/dts/sun4i-a10-a1000.dts
> @@ -196,6 +196,40 @@
> };
> };
>
> +#include "axp209.dtsi"
> +
> +®_dcdc2 {
> + regulator-always-on;
> + regulator-min-microvolt = <1000000>;
> + regulator-max-microvolt = <1400000>;
> + regulator-name = "vdd-cpu";
> +};
> +
> +®_dcdc3 {
> + regulator-always-on;
> + regulator-min-microvolt = <1000000>;
> + regulator-max-microvolt = <1250000>;
> + regulator-name = "vdd-int-dll";
> +};
> +
> +®_ldo1 {
> + regulator-name = "vdd-rtc";
> +};
> +
> +®_ldo2 {
> + regulator-always-on;
> + regulator-min-microvolt = <3000000>;
> + regulator-max-microvolt = <3000000>;
> + regulator-name = "avcc";
> +};
> +
> +®_ldo3 {
> + regulator-always-on;
> + regulator-min-microvolt = <2800000>;
> + regulator-max-microvolt = <2800000>;
> + regulator-name = "vcc-wifi";
If this is used only for the wifi, there's no point in keeping it
enabled.
Also, taking the chance to enable cpufreq by setting the cpu-suplly
property would be a good idea.
Thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161220/6f56d8f4/attachment-0001.sig>
^ permalink raw reply
* [PATCH 1/7] Documentation: DT: bindings: iio: adc: add documentation for Allwinner SoCs' GPADC driver
From: Maxime Ripard @ 2016-12-20 14:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161220102709.9504-2-quentin.schulz@free-electrons.com>
Hi,
On Tue, Dec 20, 2016 at 11:27:03AM +0100, Quentin Schulz wrote:
> The Allwinner SoCs all have an ADC that can also act as a thermal sensor
> and sometimes as a touchscreen controller. If there is a touchscreen
> controller, the first four channels can be used either for the ADC or
> the touchscreen and the fifth channel is used for the thermal sensor.
> If there is not a touchscreen controller, the one and only channel is
> used for the thermal sensor.
>
> This patch adds the documentation for the driver of the Allwinner SoCs'
> GPADC.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> ---
> .../bindings/iio/adc/sun4i-gpadc-iio.txt | 57 ++++++++++++++++++++++
> 1 file changed, 57 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/sun4i-gpadc-iio.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/sun4i-gpadc-iio.txt b/Documentation/devicetree/bindings/iio/adc/sun4i-gpadc-iio.txt
> new file mode 100644
> index 0000000..aab768d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/sun4i-gpadc-iio.txt
> @@ -0,0 +1,57 @@
> +Allwinner SoCs' GPADC Device Tree bindings
> +------------------------------------------
> +
> +The Allwinner SoCs all have an ADC that can also act as a thermal sensor and
> +sometimes as a touchscreen controller. If there is a touchscreen controller, the
> +first four channels can be used either for the ADC or the touchscreen and the
> +fifth channel is used for the thermal sensor.
> +If there is not a touchscreen controller, the one and only channel is used for
> +the thermal sensor.
> +
> +Currently, the touchscreen controller does not have a driver using this ADC
> +driver. The touchscreen controller is currently driven only by
> +input/touchscreen/sun4i-ts.c which is absolutely incompatible with this driver.
> +
> +The Allwinner A10, A13 and A31 SoCs already have a DT binding for the
> +aforementioned input driver, thus an MFD driver matches the existing DT binding
> +(mfd/sun4i-gpadc.c) and replaces the input driver. No DT binding is required for
> +these SoCs' ADC, everything is handled by the MFD which is matching the existing
> +DT binding for input/touchscreen/sun4i-ts.c.
> +
> +The Allwinner A33 GPADC only have a thermal sensor and have a proper DT binding
> +for this driver unlike the previously mentioned SoCs.
The DT bindings should be agnostic from the OS. You can remove all
mention of the implementations details in Linux.
(and you should wrap at 72 characters).
But we already have a binding document for that controller, so you
shouldn't create a new one, reuse the old one that is already there.
> +Required properties:
> + - compatible: "allwinner,sun8i-a33-gpadc-iio"
IIO is an implementation detail. The IP is called GPADC.
You're also missing reg.
> +
> +Optional properties:
> +(for use with thermal framework for CPU thermal throttling for example, and/or
> + IIO consumers)
> + - #thermal-sensor-cells = <0>; (see
> +Documentation/devicetree/bindings/thermal/thermal.txt)
> + - #io-channel-cells = <0>; (see
> +Documentation/devicetree/bindings/iio/iio-bindings.txt)
I wouldn't list that as optional.
Thanks,
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161220/34694221/attachment.sig>
^ permalink raw reply
* [PATCH 2/7] Documentation: DT: bindings: mfd: add documentation for Allwinner SoCs' GPADC MFD driver
From: Maxime Ripard @ 2016-12-20 14:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161220102709.9504-3-quentin.schulz@free-electrons.com>
On Tue, Dec 20, 2016 at 11:27:04AM +0100, Quentin Schulz wrote:
> The Allwinner SoCs all have an ADC that can also act as a thermal sensor
> and sometimes as a touchscreen controller. If there is a touchscreen
> controller, the first four channels can be used either for the ADC or
> the touchscreen and the fifth channel is used for the thermal sensor.
> If there is not a touchscreen controller, the one and only channel is
> used for the thermal sensor.
>
> The Allwinner SoCs already have an existing DT binding for the
> touchscreen controller and thermal sensor for the sun4i-ts input driver
> which does let the user use the ADC. To keep backward compatibility,
> this MFD driver re-uses the same bindings as the sun4i-ts input driver
> and will probe the required drivers to make the ADC and thermal sensor
> work.
>
> This patch adds the binding documentation for the MFD driver of the
> Allwinner SoCs' GPADC.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> ---
> .../devicetree/bindings/mfd/sun4i-gpadc.txt | 47 ++++++++++++++++++++++
> 1 file changed, 47 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/sun4i-gpadc.txt
>
> diff --git a/Documentation/devicetree/bindings/mfd/sun4i-gpadc.txt b/Documentation/devicetree/bindings/mfd/sun4i-gpadc.txt
> new file mode 100644
> index 0000000..bc4b4f6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/sun4i-gpadc.txt
> @@ -0,0 +1,47 @@
> +Allwinner SoCs' GPADC Device Tree bindings
> +------------------------------------------
> +
> +The Allwinner SoCs all have an ADC that can also act as a thermal sensor and
> +sometimes as a touchscreen controller. If there is a touchscreen controller, the
> +first four channels can be used either for the ADC or the touchscreen and the
> +fifth channel is used for the thermal sensor.
> +If there is not a touchscreen controller, the one and only channel is used for
> +the thermal sensor.
> +
> +Currently, the touchscreen controller does not have a driver using this ADC
> +driver. The touchscreen controller is currently driven only by
> +input/touchscreen/sun4i-ts.c which is absolutely incompatible with this driver.
> +
> +The Allwinner A10, A13 and A31 SoCs already have a DT binding for the
> +aforementioned input driver, thus this MFD driver matches the existing DT
> +binding (mfd/sun4i-gpadc.c).
> +To keep DT binding compatibility, the MFD replaces the sun4i-ts input driver and
> +probes required drivers (IIO GPADC driver (iio/adc/sun4i-gpadc-iio.c),
> +iio-hwmon and soon the touchscreen driver) without the need for a DT binding for
> +each driver.
> +
> +Required properties:
> + - compatible: one of:
> + - "allwinner,sun4i-a10-ts",
> + - "allwinner,sun5i-a13-ts",
> + - "allwinner,sun6i-a31-ts"
> + - #thermal-sensor-cells = <0>;
Same thing here, we already have such a document, please amend it if
needed.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161220/e00bb9e3/attachment.sig>
^ permalink raw reply
* [linux-sunxi][PATCH 2/3] ARM: dts: sun6i: Add the SPDIF block to the A31
From: Code Kipper @ 2016-12-20 14:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161220140709.pxvzcm3osmbromfh@lukather>
On 20 December 2016 at 15:07, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> Hi,
>
> On Tue, Dec 20, 2016 at 11:40:37AM +0100, codekipper at gmail.com wrote:
>> From: Marcus Cooper <codekipper@gmail.com>
>>
>> Add the SPDIF transceiver controller block to the A31 dtsi.
>>
>> Signed-off-by: Marcus Cooper <codekipper@gmail.com>
>> ---
>> arch/arm/boot/dts/sun6i-a31.dtsi | 14 ++++++++++++++
>> 1 file changed, 14 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
>> index 7370ba6c9993..559c53efa7e6 100644
>> --- a/arch/arm/boot/dts/sun6i-a31.dtsi
>> +++ b/arch/arm/boot/dts/sun6i-a31.dtsi
>> @@ -613,6 +613,20 @@
>> reg = <0x01c20ca0 0x20>;
>> };
>>
>> + spdif: spdif at 01c21000 {
>> + #sound-dai-cells = <0>;
>> + compatible = "allwinner,sun6i-a31-spdif";
>> + reg = <0x01c21000 0x400>;
>> + interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
>> + clocks = <&ccu CLK_APB1_SPDIF>, <&ccu CLK_SPDIF>;
>> + resets = <&ccu RST_APB1_SPDIF>;
>> + clock-names = "apb", "spdif";
>> + dmas = <&dma 2>, <&dma 2>;
>> + dma-names = "rx", "tx";
>> + spdif-out = "disabled";
>
> That property isn't documented anywhere, and doesn't seem to be used
> in your driver either.
Ooops....do you want me to respin a new patch or will you do your
magic with 'dd'? It fell through the cracks as it was cherry picked
from my dev branch where I was at one time playing with spdif-in. This
has pretty much been relegated to the bottom of my todo/finish list.
>
> On a separate topic, is the channel inversion bug also found on the
> A31?
I have seen this and I'm sure that was also on my A31 hardware but
I've just fired her up and the speaker test worked as expected. I also
repeated the test on my A10 device and didn't hear the issue.
CK
>
> Maxime
>
> --
> Maxime Ripard, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com
^ permalink raw reply
* [PATCH 3/7] iio: adc: sun4i-gpadc-iio: add support for A33 thermal sensor
From: Maxime Ripard @ 2016-12-20 14:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161220102709.9504-4-quentin.schulz@free-electrons.com>
On Tue, Dec 20, 2016 at 11:27:05AM +0100, Quentin Schulz wrote:
> This adds support for the Allwinner A33 thermal sensor.
>
> Unlike the A10, A13 and A31, the Allwinner A33 only has one channel
> which is dedicated to the thermal sensor. Moreover, its thermal sensor
> does not generate interruptions, thus we only need to directly read the
> register storing the temperature value.
>
> The MFD used by the A10, A13 and A31, was created to avoid breaking the
> DT binding, but since the nodes for the ADC weren't there for the A33,
> it is not needed.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> ---
> drivers/iio/adc/Kconfig | 21 ++--
> drivers/iio/adc/sun4i-gpadc-iio.c | 204 ++++++++++++++++++++++++++++----------
> include/linux/mfd/sun4i-gpadc.h | 4 +
> 3 files changed, 172 insertions(+), 57 deletions(-)
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 6a6d369..06041ff 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -437,17 +437,24 @@ config STX104
> config SUN4I_GPADC
> tristate "Support for the Allwinner SoCs GPADC"
> depends on IIO
> - depends on MFD_SUN4I_GPADC
> - help
> - Say yes here to build support for Allwinner (A10, A13 and A31) SoCs
> - GPADC. This ADC provides 4 channels which can be used as an ADC or as
> - a touchscreen input and one channel for thermal sensor.
> -
> - The thermal sensor slows down ADC readings and can be disabled by
> +# MFD_SUN4I_GPADC is needed for sun4i, sun5i and sun6i but not for sun8i
The indentation is wrong here, and I wouldn't list all the families
there, this is just going to be always amended, for no particular
reason.
> + select MFD_SUN4I_GPADC if MACH_SUN4I || MACH_SUN5I || MACH_SUN6I
Why did you change the depends on to a select?
And isn't that redundant with the comment just above?
> +# THERMAL_OF can be disabled on sun4i, sun5i and sun6i to quicken ADC readings
I'm not sure this is worth adding either. Any option can be added with
any number of side effects, I'm not sure we want to list all of them.
> + depends on THERMAL_OF || MACH_SUN4I || MACH_SUN5I || MACH_SUN6I
So you can't disable THERMAL_OF on MACH_SUN8I?
> + depends on !TOUCHSCREEN_SUN4I
This should be a different patch.
> + help
> + Say yes here to build support for Allwinner (A10, A13, A31 and A33)
> + SoCs GPADC.
> +
> + The ADC on A10, A13 and A31 provides 4 channels which can be used as
> + an ADC or as a touchscreen input and one channel for thermal sensor.
> + Their thermal sensor slows down ADC readings and can be disabled by
Again, I'm not sure putting all those details in the Kconfig help
really helps. This is only going to grow with more and more cases, and
this isn't something really helpful anyway.
> disabling CONFIG_THERMAL_OF. However, the thermal sensor should be
> enabled by default since the SoC temperature is usually more critical
> than ADC readings.
>
> + The ADC on A33 provides one channel for thermal sensor.
> +
> To compile this driver as a module, choose M here: the module will be
> called sun4i-gpadc-iio.
>
> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
> index a8e134f..8be694e 100644
> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
> @@ -1,4 +1,4 @@
> -/* ADC driver for sunxi platforms' (A10, A13 and A31) GPADC
> +/* ADC driver for sunxi platforms' (A10, A13, A31 and A33) GPADC
> *
> * Copyright (c) 2016 Quentin Schulz <quentin.schulz@free-electrons.com>
> *
> @@ -85,6 +85,12 @@ static const struct gpadc_data sun6i_gpadc_data = {
> .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
> };
>
> +static const struct gpadc_data sun8i_gpadc_data = {
> + .temp_offset = -1662,
> + .temp_scale = 162,
> + .tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN,
> +};
> +
> struct sun4i_gpadc_iio {
> struct iio_dev *indio_dev;
> struct completion completion;
> @@ -96,6 +102,7 @@ struct sun4i_gpadc_iio {
> unsigned int temp_data_irq;
> atomic_t ignore_temp_data_irq;
> const struct gpadc_data *data;
> + bool use_dt;
> /* prevents concurrent reads of temperature and ADC */
> struct mutex mutex;
> };
> @@ -138,6 +145,23 @@ static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = {
> SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
> };
>
> +static const struct iio_chan_spec sun8i_gpadc_channels[] = {
sun8i_a33. Other SoCs from that same family might have different
channels.
> + {
> + .type = IIO_TEMP,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> + BIT(IIO_CHAN_INFO_SCALE) |
> + BIT(IIO_CHAN_INFO_OFFSET),
> + .datasheet_name = "temp_adc",
> + },
> +};
> +
> +static const struct regmap_config sun4i_gpadc_regmap_config = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .fast_io = true,
> +};
> +
> static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel,
> unsigned int irq)
> {
> @@ -231,7 +255,6 @@ static int sun4i_gpadc_read(struct iio_dev *indio_dev, int channel, int *val,
> err:
> pm_runtime_put_autosuspend(indio_dev->dev.parent);
> mutex_unlock(&info->mutex);
> -
> return ret;
> }
>
> @@ -246,6 +269,19 @@ static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel,
> static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
> {
> struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> + int ret;
> +
> + if (info->use_dt) {
> + pm_runtime_get_sync(indio_dev->dev.parent);
> +
> + ret = regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
> + if (!ret)
> + pm_runtime_mark_last_busy(indio_dev->dev.parent);
> +
> + pm_runtime_put_autosuspend(indio_dev->dev.parent);
> +
> + return 0;
> + }
Why is runtime_pm linked to the DT support or not?
>
> return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
> }
> @@ -410,7 +446,7 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name,
> unsigned int *irq, atomic_t *atomic)
> {
> int ret;
> - struct sun4i_gpadc_dev *mfd_dev = dev_get_drvdata(pdev->dev.parent);
> + struct sun4i_gpadc_dev *mfd_dev;
> struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(&pdev->dev));
>
> /*
> @@ -427,6 +463,8 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name,
> */
> atomic_set(atomic, 1);
>
> + mfd_dev = dev_get_drvdata(pdev->dev.parent);
> +
Why is that change necessary?
> ret = platform_get_irq_byname(pdev, name);
> if (ret < 0) {
> dev_err(&pdev->dev, "no %s interrupt registered\n", name);
> @@ -454,31 +492,68 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name,
> return 0;
> }
>
> -static int sun4i_gpadc_probe(struct platform_device *pdev)
> +static const struct of_device_id sun4i_gpadc_of_id[] = {
> + {
> + .compatible = "allwinner,sun8i-a33-gpadc-iio",
> + .data = &sun8i_gpadc_data,
> + },
> + { /* sentinel */ }
> +};
> +
> +static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
> + struct iio_dev *indio_dev)
> {
> - struct sun4i_gpadc_iio *info;
> - struct iio_dev *indio_dev;
> + struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> + const struct of_device_id *of_dev;
> + struct thermal_zone_device *tzd;
> + struct resource *mem;
> + void __iomem *base;
> int ret;
> - struct sun4i_gpadc_dev *sun4i_gpadc_dev;
>
> - sun4i_gpadc_dev = dev_get_drvdata(pdev->dev.parent);
> + of_dev = of_match_device(sun4i_gpadc_of_id, &pdev->dev);
> + if (!of_dev)
> + return -ENODEV;
> +
> + info->use_dt = true;
> + info->data = (struct gpadc_data *)of_dev->data;
> + indio_dev->num_channels = ARRAY_SIZE(sun8i_gpadc_channels);
> + indio_dev->channels = sun8i_gpadc_channels;
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + base = devm_ioremap_resource(&pdev->dev, mem);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + info->regmap = devm_regmap_init_mmio(&pdev->dev, base,
> + &sun4i_gpadc_regmap_config);
> + if (IS_ERR(info->regmap)) {
> + ret = PTR_ERR(info->regmap);
> + dev_err(&pdev->dev, "failed to init regmap: %d\n", ret);
> + return ret;
> + }
>
> - indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> - if (!indio_dev)
> - return -ENOMEM;
> + tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, info,
> + &sun4i_ts_tz_ops);
> + if (IS_ERR(tzd)) {
> + dev_err(&pdev->dev, "could not register thermal sensor: %ld\n",
> + PTR_ERR(tzd));
> + return PTR_ERR(tzd);
> + }
>
> - info = iio_priv(indio_dev);
> - platform_set_drvdata(pdev, indio_dev);
> + return 0;
> +}
>
> - mutex_init(&info->mutex);
> +static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
> + struct iio_dev *indio_dev)
> +{
> + struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> + struct sun4i_gpadc_dev *sun4i_gpadc_dev;
> + int ret;
> +
> + info->use_dt = false;
> + sun4i_gpadc_dev = dev_get_drvdata(pdev->dev.parent);
> info->regmap = sun4i_gpadc_dev->regmap;
> - info->indio_dev = indio_dev;
> - init_completion(&info->completion);
> - indio_dev->name = dev_name(&pdev->dev);
> - indio_dev->dev.parent = &pdev->dev;
> - indio_dev->dev.of_node = pdev->dev.of_node;
> - indio_dev->info = &sun4i_gpadc_iio_info;
> - indio_dev->modes = INDIO_DIRECT_MODE;
> +
You should have two patches. One to split the MFD part from the probe,
and a second one to add the DT probing.
> indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels);
> indio_dev->channels = sun4i_gpadc_channels;
>
> @@ -494,7 +569,6 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
> * register the sensor if that option is enabled, eventually leaving
> * that choice to the user.
> */
> -
Spurious change?
> if (IS_ENABLED(CONFIG_THERMAL_OF)) {
> /*
> * This driver is a child of an MFD which has a node in the DT
> @@ -519,8 +593,7 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
> dev_err(&pdev->dev,
> "could not register thermal sensor: %ld\n",
> PTR_ERR(tzd));
> - ret = PTR_ERR(tzd);
> - goto err;
> + return PTR_ERR(tzd);
> }
> } else {
> indio_dev->num_channels =
> @@ -528,49 +601,78 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
> indio_dev->channels = sun4i_gpadc_channels_no_temp;
> }
>
> - pm_runtime_set_autosuspend_delay(&pdev->dev,
> - SUN4I_GPADC_AUTOSUSPEND_DELAY);
> - pm_runtime_use_autosuspend(&pdev->dev);
> - pm_runtime_set_suspended(&pdev->dev);
> - pm_runtime_enable(&pdev->dev);
> -
> if (IS_ENABLED(CONFIG_THERMAL_OF)) {
> ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING",
> sun4i_gpadc_temp_data_irq_handler,
> "temp_data", &info->temp_data_irq,
> &info->ignore_temp_data_irq);
> if (ret < 0)
> - goto err;
> - }
> + return ret;
>
> - ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
> - sun4i_gpadc_fifo_data_irq_handler, "fifo_data",
> - &info->fifo_data_irq, &info->ignore_fifo_data_irq);
> - if (ret < 0)
> - goto err;
> + ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
> + sun4i_gpadc_fifo_data_irq_handler,
> + "fifo_data", &info->fifo_data_irq,
> + &info->ignore_fifo_data_irq);
> + if (ret < 0)
> + return ret;
> + }
>
> - if (IS_ENABLED(CONFIG_THERMAL_OF)) {
> - ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
> - if (ret < 0) {
> - dev_err(&pdev->dev,
> - "failed to register iio map array\n");
> - goto err;
> - }
> + ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "failed to register iio map array\n");
> + return ret;
> }
>
> + return 0;
> +}
> +
> +static int sun4i_gpadc_probe(struct platform_device *pdev)
> +{
> + struct sun4i_gpadc_iio *info;
> + struct iio_dev *indio_dev;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + info = iio_priv(indio_dev);
> + platform_set_drvdata(pdev, indio_dev);
> +
> + mutex_init(&info->mutex);
> + info->indio_dev = indio_dev;
> + init_completion(&info->completion);
> + indio_dev->name = dev_name(&pdev->dev);
> + indio_dev->dev.parent = &pdev->dev;
> + indio_dev->dev.of_node = pdev->dev.of_node;
> + indio_dev->info = &sun4i_gpadc_iio_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + if (pdev->dev.of_node)
> + ret = sun4i_gpadc_probe_dt(pdev, indio_dev);
> + else
> + ret = sun4i_gpadc_probe_mfd(pdev, indio_dev);
> +
> + if (ret)
> + return ret;
> +
> + pm_runtime_set_autosuspend_delay(&pdev->dev,
> + SUN4I_GPADC_AUTOSUSPEND_DELAY);
> + pm_runtime_use_autosuspend(&pdev->dev);
> + pm_runtime_set_suspended(&pdev->dev);
> + pm_runtime_enable(&pdev->dev);
> +
> ret = devm_iio_device_register(&pdev->dev, indio_dev);
> if (ret < 0) {
> dev_err(&pdev->dev, "could not register the device\n");
> - goto err_map;
> + goto err;
> }
>
> return 0;
>
> -err_map:
> - if (IS_ENABLED(CONFIG_THERMAL_OF))
> - iio_map_array_unregister(indio_dev);
> -
> err:
> + if (!info->use_dt && IS_ENABLED(CONFIG_THERMAL_OF))
> + iio_map_array_unregister(indio_dev);
> pm_runtime_put(&pdev->dev);
> pm_runtime_disable(&pdev->dev);
>
> @@ -580,10 +682,11 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
> static int sun4i_gpadc_remove(struct platform_device *pdev)
> {
> struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> + struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
>
> pm_runtime_put(&pdev->dev);
> pm_runtime_disable(&pdev->dev);
> - if (IS_ENABLED(CONFIG_THERMAL_OF))
> + if (!info->use_dt && IS_ENABLED(CONFIG_THERMAL_OF))
> iio_map_array_unregister(indio_dev);
>
> return 0;
> @@ -599,6 +702,7 @@ static const struct platform_device_id sun4i_gpadc_id[] = {
> static struct platform_driver sun4i_gpadc_driver = {
> .driver = {
> .name = "sun4i-gpadc-iio",
> + .of_match_table = sun4i_gpadc_of_id,
> .pm = &sun4i_gpadc_pm_ops,
> },
> .id_table = sun4i_gpadc_id,
> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
> index 509e736..139872c 100644
> --- a/include/linux/mfd/sun4i-gpadc.h
> +++ b/include/linux/mfd/sun4i-gpadc.h
> @@ -38,6 +38,10 @@
> #define SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(x) (GENMASK(3, 0) & BIT(x))
> #define SUN6I_GPADC_CTRL1_ADC_CHAN_MASK GENMASK(3, 0)
>
> +/* TP_CTRL1 bits for sun8i SoCs */
> +#define SUN8I_GPADC_CTRL1_CHOP_TEMP_EN BIT(8)
> +#define SUN8I_GPADC_CTRL1_GPADC_CALI_EN BIT(7)
> +
> #define SUN4I_GPADC_CTRL2 0x08
>
> #define SUN4I_GPADC_CTRL2_TP_SENSITIVE_ADJUST(x) ((GENMASK(3, 0) & (x)) << 28)
> --
> 2.9.3
>
Thanks,
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161220/c8014a12/attachment-0001.sig>
^ permalink raw reply
* [PATCH 0/2] ASoC: sun4i-spdif: Changes in preparation of supporting new SoCs
From: codekipper at gmail.com @ 2016-12-20 14:49 UTC (permalink / raw)
To: linux-arm-kernel
From: Marcus Cooper <codekipper@gmail.com>
Hi All,
please find attached two patches. First being a cleanup of something that
shouldn't be in the code and the second adds the mechanism for supporting
newer Allwiner SoCs. There was a third patch in the series and that was
bringing in support of SPDIF on the Allwinner H3 but I've not been able to
test it so that I hear audio. As soon as I get some hardware with a SPDIF
connection I will confirm and push.
BR,
CK
Marcus Cooper (2):
ASoC: sun4i-spdif: remove legacy dapm components
ASoC: sun4i-spdif: Add quirks to the spdif driver
sound/soc/sunxi/sun4i-spdif.c | 36 +++++++++++++++++++++++++++---------
1 file changed, 27 insertions(+), 9 deletions(-)
--
2.11.0
^ permalink raw reply
* [PATCH 1/2] ASoC: sun4i-spdif: remove legacy dapm components
From: codekipper at gmail.com @ 2016-12-20 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161220144914.30945-1-codekipper@gmail.com>
From: Marcus Cooper <codekipper@gmail.com>
The dapm components are now handled by the ALSA SoC SPDIF DIT driver
so can be removed.
Signed-off-by: Marcus Cooper <codekipper@gmail.com>
---
sound/soc/sunxi/sun4i-spdif.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
index 88fbb3a1e660..048de15d6937 100644
--- a/sound/soc/sunxi/sun4i-spdif.c
+++ b/sound/soc/sunxi/sun4i-spdif.c
@@ -403,14 +403,6 @@ static struct snd_soc_dai_driver sun4i_spdif_dai = {
.name = "spdif",
};
-static const struct snd_soc_dapm_widget dit_widgets[] = {
- SND_SOC_DAPM_OUTPUT("spdif-out"),
-};
-
-static const struct snd_soc_dapm_route dit_routes[] = {
- { "spdif-out", NULL, "Playback" },
-};
-
static const struct of_device_id sun4i_spdif_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-spdif", },
{ .compatible = "allwinner,sun6i-a31-spdif", },
--
2.11.0
^ permalink raw reply related
* [PATCH 2/2] ASoC: sun4i-spdif: Add quirks to the spdif driver
From: codekipper at gmail.com @ 2016-12-20 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161220144914.30945-1-codekipper@gmail.com>
From: Marcus Cooper <codekipper@gmail.com>
It has been seen that some newer SoCs have a different TX FIFO
address and we already have the difference with the A31 requiring
a reset. Add a quirks structure so that these can be managed
easily.
Signed-off-by: Marcus Cooper <codekipper@gmail.com>
---
sound/soc/sunxi/sun4i-spdif.c | 36 +++++++++++++++++++++++++++++++-----
1 file changed, 31 insertions(+), 5 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
index 048de15d6937..fec62ee1fc72 100644
--- a/sound/soc/sunxi/sun4i-spdif.c
+++ b/sound/soc/sunxi/sun4i-spdif.c
@@ -403,9 +403,29 @@ static struct snd_soc_dai_driver sun4i_spdif_dai = {
.name = "spdif",
};
+struct sun4i_spdif_quirks {
+ unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */
+ bool has_reset;
+};
+
+static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = {
+ .reg_dac_txdata = SUN4I_SPDIF_TXFIFO,
+};
+
+static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = {
+ .reg_dac_txdata = SUN4I_SPDIF_TXFIFO,
+ .has_reset = true,
+};
+
static const struct of_device_id sun4i_spdif_of_match[] = {
- { .compatible = "allwinner,sun4i-a10-spdif", },
- { .compatible = "allwinner,sun6i-a31-spdif", },
+ {
+ .compatible = "allwinner,sun4i-a10-spdif",
+ .data = &sun4i_a10_spdif_quirks,
+ },
+ {
+ .compatible = "allwinner,sun6i-a31-spdif",
+ .data = &sun6i_a31_spdif_quirks,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match);
@@ -438,6 +458,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
{
struct sun4i_spdif_dev *host;
struct resource *res;
+ const struct sun4i_spdif_quirks *quirks;
int ret;
void __iomem *base;
@@ -459,6 +480,12 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
+ quirks = of_device_get_match_data(&pdev->dev);
+ if (quirks == NULL) {
+ dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
+ return -ENODEV;
+ }
+
host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&sun4i_spdif_regmap_config);
@@ -476,14 +503,13 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
goto err_disable_apb_clk;
}
- host->dma_params_tx.addr = res->start + SUN4I_SPDIF_TXFIFO;
+ host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata;
host->dma_params_tx.maxburst = 8;
host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
platform_set_drvdata(pdev, host);
- if (of_device_is_compatible(pdev->dev.of_node,
- "allwinner,sun6i-a31-spdif")) {
+ if (quirks->has_reset) {
host->rst = devm_reset_control_get_optional(&pdev->dev, NULL);
if (IS_ERR(host->rst) && PTR_ERR(host->rst) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
--
2.11.0
^ permalink raw reply related
* [PATCH] ASoC: sun4i-i2s: Add quirks for newer SoCs
From: codekipper at gmail.com @ 2016-12-20 14:55 UTC (permalink / raw)
To: linux-arm-kernel
From: Marcus Cooper <codekipper@gmail.com>
Newer SoCs have additional functionality so a quirks structure
has been added to handle them. So far we've seen the use of a
reset controller, a different address for the TXFIFO and varying
register changes.
This patch prepares the driver for these changes and adds the
reset specifier.
Signed-off-by: Marcus Cooper <codekipper@gmail.com>
---
.../devicetree/bindings/sound/sun4i-i2s.txt | 2 +
sound/soc/sunxi/sun4i-i2s.c | 47 ++++++++++++++++++++--
2 files changed, 45 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt
index 7a2c0945fd22..494a881ccd21 100644
--- a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt
+++ b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt
@@ -18,6 +18,8 @@ Required properties:
- "apb" : clock for the I2S bus interface
- "mod" : module clock for the I2S controller
- #sound-dai-cells : Must be equal to 0
+- resets: reset specifier for the ahb reset (A31 and newer only)
+
Example:
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index f24d19526603..80fe4f1d6e3b 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -14,9 +14,11 @@
#include <linux/clk.h>
#include <linux/dmaengine.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+#include <linux/reset.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
@@ -92,6 +94,7 @@ struct sun4i_i2s {
struct clk *bus_clk;
struct clk *mod_clk;
struct regmap *regmap;
+ struct reset_control *rst;
unsigned int mclk_freq;
@@ -104,6 +107,13 @@ struct sun4i_i2s_clk_div {
u8 val;
};
+struct sun4i_i2s_quirks {
+ unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */
+ bool has_reset;
+ const struct regmap_config *sun4i_i2s_regmap;
+ const struct snd_soc_dai_ops *ops;
+};
+
static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = {
{ .div = 2, .val = 0 },
{ .div = 4, .val = 1 },
@@ -541,7 +551,6 @@ static struct snd_soc_dai_driver sun4i_i2s_dai = {
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = &sun4i_i2s_dai_ops,
.symmetric_rates = 1,
};
@@ -655,6 +664,7 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
{
struct sun4i_i2s *i2s;
struct resource *res;
+ const struct sun4i_i2s_quirks *quirks;
void __iomem *regs;
int irq, ret;
@@ -680,8 +690,14 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
return PTR_ERR(i2s->bus_clk);
}
+ quirks = of_device_get_match_data(&pdev->dev);
+ if (quirks == NULL) {
+ dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
+ return -ENODEV;
+ }
+
i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
- &sun4i_i2s_regmap_config);
+ quirks->sun4i_i2s_regmap);
if (IS_ERR(i2s->regmap)) {
dev_err(&pdev->dev, "Regmap initialisation failed\n");
return PTR_ERR(i2s->regmap);
@@ -692,13 +708,25 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Can't get our mod clock\n");
return PTR_ERR(i2s->mod_clk);
}
+
- i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
+ i2s->playback_dma_data.addr = res->start + quirks->reg_dac_txdata;
i2s->playback_dma_data.maxburst = 4;
i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG;
i2s->capture_dma_data.maxburst = 4;
+ if (quirks->has_reset) {
+ i2s->rst = devm_reset_control_get_optional(&pdev->dev, NULL);
+ if (IS_ERR(i2s->rst) && PTR_ERR(i2s->rst) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);
+ goto err_pm_disable;
+ }
+ if (!IS_ERR(i2s->rst))
+ reset_control_deassert(i2s->rst);
+ }
+
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = sun4i_i2s_runtime_resume(&pdev->dev);
@@ -706,6 +734,8 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
goto err_pm_disable;
}
+ /* Register ops with dai */
+ sun4i_i2s_dai.ops = quirks->ops;
ret = devm_snd_soc_register_component(&pdev->dev,
&sun4i_i2s_component,
&sun4i_i2s_dai, 1);
@@ -742,8 +772,17 @@ static int sun4i_i2s_remove(struct platform_device *pdev)
return 0;
}
+static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
+ .reg_dac_txdata = SUN4I_I2S_FIFO_TX_REG,
+ .sun4i_i2s_regmap = &sun4i_i2s_regmap_config,
+ .ops = &sun4i_i2s_dai_ops,
+};
+
static const struct of_device_id sun4i_i2s_match[] = {
- { .compatible = "allwinner,sun4i-a10-i2s", },
+ {
+ .compatible = "allwinner,sun4i-a10-i2s",
+ .data = &sun4i_a10_i2s_quirks,
+ },
{}
};
MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
--
2.11.0
^ permalink raw reply related
* [RFC PATCH] vring: Force use of DMA API for ARM-based systems
From: Will Deacon @ 2016-12-20 15:14 UTC (permalink / raw)
To: linux-arm-kernel
Booting Linux on an ARM fastmodel containing an SMMU emulation results
in an unexpected I/O page fault from the legacy virtio-blk PCI device:
[ 1.211721] arm-smmu-v3 2b400000.smmu: event 0x10 received:
[ 1.211800] arm-smmu-v3 2b400000.smmu: 0x00000000fffff010
[ 1.211880] arm-smmu-v3 2b400000.smmu: 0x0000020800000000
[ 1.211959] arm-smmu-v3 2b400000.smmu: 0x00000008fa081002
[ 1.212075] arm-smmu-v3 2b400000.smmu: 0x0000000000000000
[ 1.212155] arm-smmu-v3 2b400000.smmu: event 0x10 received:
[ 1.212234] arm-smmu-v3 2b400000.smmu: 0x00000000fffff010
[ 1.212314] arm-smmu-v3 2b400000.smmu: 0x0000020800000000
[ 1.212394] arm-smmu-v3 2b400000.smmu: 0x00000008fa081000
[ 1.212471] arm-smmu-v3 2b400000.smmu: 0x0000000000000000
<system hangs failing to read partition table>
This is because the virtio-blk is behind an SMMU, so we have consequently
swizzled its DMA ops and configured the SMMU to translate accesses. This
then requires the vring code to use the DMA API to establish translations,
otherwise all transactions will result in fatal faults and termination.
Given that ARM-based systems only see an SMMU if one is really present
(the topology is all described by firmware tables such as device-tree or
IORT), then we can safely use the DMA API for all virtio devices.
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
drivers/virtio/virtio_ring.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index ed9c9eeedfe5..06b91e29d1b7 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -159,6 +159,10 @@ static bool vring_use_dma_api(struct virtio_device *vdev)
if (xen_domain())
return true;
+ /* On ARM-based machines, the DMA ops will do the right thing */
+ if (IS_ENABLED(CONFIG_ARM) || IS_ENABLED(CONFIG_ARM64))
+ return true;
+
return false;
}
--
2.1.4
^ permalink raw reply related
* [PATCH 3/7] iio: adc: sun4i-gpadc-iio: add support for A33 thermal sensor
From: Quentin Schulz @ 2016-12-20 15:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161220144434.2u6ivige6kdto6an@lukather>
Hi,
On 20/12/2016 15:44, Maxime Ripard wrote:
> On Tue, Dec 20, 2016 at 11:27:05AM +0100, Quentin Schulz wrote:
>> This adds support for the Allwinner A33 thermal sensor.
>>
>> Unlike the A10, A13 and A31, the Allwinner A33 only has one channel
>> which is dedicated to the thermal sensor. Moreover, its thermal sensor
>> does not generate interruptions, thus we only need to directly read the
>> register storing the temperature value.
>>
>> The MFD used by the A10, A13 and A31, was created to avoid breaking the
>> DT binding, but since the nodes for the ADC weren't there for the A33,
>> it is not needed.
>>
>> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
>> ---
>> drivers/iio/adc/Kconfig | 21 ++--
>> drivers/iio/adc/sun4i-gpadc-iio.c | 204 ++++++++++++++++++++++++++++----------
>> include/linux/mfd/sun4i-gpadc.h | 4 +
>> 3 files changed, 172 insertions(+), 57 deletions(-)
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 6a6d369..06041ff 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -437,17 +437,24 @@ config STX104
>> config SUN4I_GPADC
>> tristate "Support for the Allwinner SoCs GPADC"
>> depends on IIO
>> - depends on MFD_SUN4I_GPADC
>> - help
>> - Say yes here to build support for Allwinner (A10, A13 and A31) SoCs
>> - GPADC. This ADC provides 4 channels which can be used as an ADC or as
>> - a touchscreen input and one channel for thermal sensor.
>> -
>> - The thermal sensor slows down ADC readings and can be disabled by
>> +# MFD_SUN4I_GPADC is needed for sun4i, sun5i and sun6i but not for sun8i
>
> The indentation is wrong here, and I wouldn't list all the families
> there, this is just going to be always amended, for no particular
> reason.
>
ACK.
>> + select MFD_SUN4I_GPADC if MACH_SUN4I || MACH_SUN5I || MACH_SUN6I
>
> Why did you change the depends on to a select?
>
The "depends on" does not have an if condition but "select" has. See:
https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt
> And isn't that redundant with the comment just above?
>
>> +# THERMAL_OF can be disabled on sun4i, sun5i and sun6i to quicken ADC readings
>
> I'm not sure this is worth adding either. Any option can be added with
> any number of side effects, I'm not sure we want to list all of them.
>
ACK.
>> + depends on THERMAL_OF || MACH_SUN4I || MACH_SUN5I || MACH_SUN6I
>
> So you can't disable THERMAL_OF on MACH_SUN8I?
>
Not in the current state. devm_thermal_zone_of_sensor_register from
sun4i_gpadc_probe_dt returns an error if THERMAL_OF is disabled and
thus, the probe fails. Maybe it should rather fail silently and let the
user choose whether (s)he wants the thermal framework to be able to read
data from this driver?
>> + depends on !TOUCHSCREEN_SUN4I
>
> This should be a different patch.
>
>> + help
>> + Say yes here to build support for Allwinner (A10, A13, A31 and A33)
>> + SoCs GPADC.
>> +
>> + The ADC on A10, A13 and A31 provides 4 channels which can be used as
>> + an ADC or as a touchscreen input and one channel for thermal sensor.
>> + Their thermal sensor slows down ADC readings and can be disabled by
>
> Again, I'm not sure putting all those details in the Kconfig help
> really helps. This is only going to grow with more and more cases, and
> this isn't something really helpful anyway.
>
Are you suggesting to remove completely the paragraph on why it is
possible to disable CONFIG_THERMAL_OF for the A10, A13 and A31 or only
to remove the mention to SoCs?
>> disabling CONFIG_THERMAL_OF. However, the thermal sensor should be
>> enabled by default since the SoC temperature is usually more critical
>> than ADC readings.
>>
>> + The ADC on A33 provides one channel for thermal sensor.
>> +
>> To compile this driver as a module, choose M here: the module will be
>> called sun4i-gpadc-iio.
>>
>> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
>> index a8e134f..8be694e 100644
>> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
>> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
>> @@ -1,4 +1,4 @@
>> -/* ADC driver for sunxi platforms' (A10, A13 and A31) GPADC
>> +/* ADC driver for sunxi platforms' (A10, A13, A31 and A33) GPADC
>> *
>> * Copyright (c) 2016 Quentin Schulz <quentin.schulz@free-electrons.com>
>> *
>> @@ -85,6 +85,12 @@ static const struct gpadc_data sun6i_gpadc_data = {
>> .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
>> };
>>
>> +static const struct gpadc_data sun8i_gpadc_data = {
>> + .temp_offset = -1662,
>> + .temp_scale = 162,
>> + .tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN,
>> +};
>> +
>> struct sun4i_gpadc_iio {
>> struct iio_dev *indio_dev;
>> struct completion completion;
>> @@ -96,6 +102,7 @@ struct sun4i_gpadc_iio {
>> unsigned int temp_data_irq;
>> atomic_t ignore_temp_data_irq;
>> const struct gpadc_data *data;
>> + bool use_dt;
>> /* prevents concurrent reads of temperature and ADC */
>> struct mutex mutex;
>> };
>> @@ -138,6 +145,23 @@ static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = {
>> SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
>> };
>>
>> +static const struct iio_chan_spec sun8i_gpadc_channels[] = {
>
> sun8i_a33. Other SoCs from that same family might have different
> channels.
>
ACK.
>> + {
>> + .type = IIO_TEMP,
>> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> + BIT(IIO_CHAN_INFO_SCALE) |
>> + BIT(IIO_CHAN_INFO_OFFSET),
>> + .datasheet_name = "temp_adc",
>> + },
>> +};
>> +
>> +static const struct regmap_config sun4i_gpadc_regmap_config = {
>> + .reg_bits = 32,
>> + .val_bits = 32,
>> + .reg_stride = 4,
>> + .fast_io = true,
>> +};
>> +
>> static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel,
>> unsigned int irq)
>> {
>> @@ -231,7 +255,6 @@ static int sun4i_gpadc_read(struct iio_dev *indio_dev, int channel, int *val,
>> err:
>> pm_runtime_put_autosuspend(indio_dev->dev.parent);
>> mutex_unlock(&info->mutex);
>> -
>> return ret;
>> }
>>
>> @@ -246,6 +269,19 @@ static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel,
>> static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
>> {
>> struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
>> + int ret;
>> +
>> + if (info->use_dt) {
>> + pm_runtime_get_sync(indio_dev->dev.parent);
>> +
>> + ret = regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
>> + if (!ret)
>> + pm_runtime_mark_last_busy(indio_dev->dev.parent);
>> +
>> + pm_runtime_put_autosuspend(indio_dev->dev.parent);
>> +
>> + return 0;
>> + }
>
> Why is runtime_pm linked to the DT support or not?
>
>>
>> return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
>> }
The same runtime_pm functions are called when the driver is not probed
from DT.
sun4i_gpadc_read will call sun4i_prepare_for_irq which does a
pm_runtime_get_sync and then at the end of sun4i_gpadc_read,
pm_runtime_mark_last_busy and pm_runtime_put_autosuspend are called.
I just noticed I forgot to add a comment on this one. The A33
documentation tells us there is an interrupt for the thermal sensor but
after struggling with it, it is just false. I validated my guess with
Allwinner Linux kernel which does not wait an interrupt to read the data
register of the thermal sensor.
sun4i_gpadc_read always wait for an interrupt before reading data regs,
so I'm just not calling it and doing the "logic" (reading the data reg
and interfacing with runtime_pm) directly here in sun4i_gpadc_temp.
I'll add a comment on this one. Maybe I should create a function just
for the "logic" of the A33 thermal sensor, so it is less weird than
currently.
>> @@ -410,7 +446,7 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name,
>> unsigned int *irq, atomic_t *atomic)
>> {
>> int ret;
>> - struct sun4i_gpadc_dev *mfd_dev = dev_get_drvdata(pdev->dev.parent);
>> + struct sun4i_gpadc_dev *mfd_dev;
>> struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(&pdev->dev));
>>
>> /*
>> @@ -427,6 +463,8 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name,
>> */
>> atomic_set(atomic, 1);
>>
>> + mfd_dev = dev_get_drvdata(pdev->dev.parent);
>> +
>
> Why is that change necessary?
>
>> ret = platform_get_irq_byname(pdev, name);
>> if (ret < 0) {
>> dev_err(&pdev->dev, "no %s interrupt registered\n", name);
>> @@ -454,31 +492,68 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name,
>> return 0;
>> }
>>
>> -static int sun4i_gpadc_probe(struct platform_device *pdev)
>> +static const struct of_device_id sun4i_gpadc_of_id[] = {
>> + {
>> + .compatible = "allwinner,sun8i-a33-gpadc-iio",
>> + .data = &sun8i_gpadc_data,
>> + },
>> + { /* sentinel */ }
>> +};
>> +
>> +static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
>> + struct iio_dev *indio_dev)
>> {
>> - struct sun4i_gpadc_iio *info;
>> - struct iio_dev *indio_dev;
>> + struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
>> + const struct of_device_id *of_dev;
>> + struct thermal_zone_device *tzd;
>> + struct resource *mem;
>> + void __iomem *base;
>> int ret;
>> - struct sun4i_gpadc_dev *sun4i_gpadc_dev;
>>
>> - sun4i_gpadc_dev = dev_get_drvdata(pdev->dev.parent);
>> + of_dev = of_match_device(sun4i_gpadc_of_id, &pdev->dev);
>> + if (!of_dev)
>> + return -ENODEV;
>> +
>> + info->use_dt = true;
>> + info->data = (struct gpadc_data *)of_dev->data;
>> + indio_dev->num_channels = ARRAY_SIZE(sun8i_gpadc_channels);
>> + indio_dev->channels = sun8i_gpadc_channels;
>> +
>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + base = devm_ioremap_resource(&pdev->dev, mem);
>> + if (IS_ERR(base))
>> + return PTR_ERR(base);
>> +
>> + info->regmap = devm_regmap_init_mmio(&pdev->dev, base,
>> + &sun4i_gpadc_regmap_config);
>> + if (IS_ERR(info->regmap)) {
>> + ret = PTR_ERR(info->regmap);
>> + dev_err(&pdev->dev, "failed to init regmap: %d\n", ret);
>> + return ret;
>> + }
>>
>> - indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
>> - if (!indio_dev)
>> - return -ENOMEM;
>> + tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, info,
>> + &sun4i_ts_tz_ops);
>> + if (IS_ERR(tzd)) {
>> + dev_err(&pdev->dev, "could not register thermal sensor: %ld\n",
>> + PTR_ERR(tzd));
>> + return PTR_ERR(tzd);
>> + }
>>
>> - info = iio_priv(indio_dev);
>> - platform_set_drvdata(pdev, indio_dev);
>> + return 0;
>> +}
>>
>> - mutex_init(&info->mutex);
>> +static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
>> + struct iio_dev *indio_dev)
>> +{
>> + struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
>> + struct sun4i_gpadc_dev *sun4i_gpadc_dev;
>> + int ret;
>> +
>> + info->use_dt = false;
>> + sun4i_gpadc_dev = dev_get_drvdata(pdev->dev.parent);
>> info->regmap = sun4i_gpadc_dev->regmap;
>> - info->indio_dev = indio_dev;
>> - init_completion(&info->completion);
>> - indio_dev->name = dev_name(&pdev->dev);
>> - indio_dev->dev.parent = &pdev->dev;
>> - indio_dev->dev.of_node = pdev->dev.of_node;
>> - indio_dev->info = &sun4i_gpadc_iio_info;
>> - indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>
> You should have two patches. One to split the MFD part from the probe,
> and a second one to add the DT probing.
>
ACK.
>> indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels);
>> indio_dev->channels = sun4i_gpadc_channels;
>>
>> @@ -494,7 +569,6 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
>> * register the sensor if that option is enabled, eventually leaving
>> * that choice to the user.
>> */
>> -
>
> Spurious change?
>
Yes, lots of. Sorry, I will be more careful on that.
>> if (IS_ENABLED(CONFIG_THERMAL_OF)) {
>> /*
>> * This driver is a child of an MFD which has a node in the DT
>> @@ -519,8 +593,7 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
>> dev_err(&pdev->dev,
>> "could not register thermal sensor: %ld\n",
>> PTR_ERR(tzd));
>> - ret = PTR_ERR(tzd);
>> - goto err;
>> + return PTR_ERR(tzd);
>> }
>> } else {
>> indio_dev->num_channels =
>> @@ -528,49 +601,78 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
>> indio_dev->channels = sun4i_gpadc_channels_no_temp;
>> }
>>
>> - pm_runtime_set_autosuspend_delay(&pdev->dev,
>> - SUN4I_GPADC_AUTOSUSPEND_DELAY);
>> - pm_runtime_use_autosuspend(&pdev->dev);
>> - pm_runtime_set_suspended(&pdev->dev);
>> - pm_runtime_enable(&pdev->dev);
>> -
>> if (IS_ENABLED(CONFIG_THERMAL_OF)) {
>> ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING",
>> sun4i_gpadc_temp_data_irq_handler,
>> "temp_data", &info->temp_data_irq,
>> &info->ignore_temp_data_irq);
>> if (ret < 0)
>> - goto err;
>> - }
>> + return ret;
>>
>> - ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
>> - sun4i_gpadc_fifo_data_irq_handler, "fifo_data",
>> - &info->fifo_data_irq, &info->ignore_fifo_data_irq);
>> - if (ret < 0)
>> - goto err;
>> + ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
>> + sun4i_gpadc_fifo_data_irq_handler,
>> + "fifo_data", &info->fifo_data_irq,
>> + &info->ignore_fifo_data_irq);
>> + if (ret < 0)
>> + return ret;
>> + }
>>
>> - if (IS_ENABLED(CONFIG_THERMAL_OF)) {
>> - ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
>> - if (ret < 0) {
>> - dev_err(&pdev->dev,
>> - "failed to register iio map array\n");
>> - goto err;
>> - }
>> + ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
>> + if (ret < 0) {
>> + dev_err(&pdev->dev, "failed to register iio map array\n");
>> + return ret;
>> }
>>
>> + return 0;
>> +}
>> +
>> +static int sun4i_gpadc_probe(struct platform_device *pdev)
>> +{
>> + struct sun4i_gpadc_iio *info;
>> + struct iio_dev *indio_dev;
>> + int ret;
>> +
>> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
>> + if (!indio_dev)
>> + return -ENOMEM;
>> +
>> + info = iio_priv(indio_dev);
>> + platform_set_drvdata(pdev, indio_dev);
>> +
>> + mutex_init(&info->mutex);
>> + info->indio_dev = indio_dev;
>> + init_completion(&info->completion);
>> + indio_dev->name = dev_name(&pdev->dev);
>> + indio_dev->dev.parent = &pdev->dev;
>> + indio_dev->dev.of_node = pdev->dev.of_node;
>> + indio_dev->info = &sun4i_gpadc_iio_info;
>> + indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> + if (pdev->dev.of_node)
>> + ret = sun4i_gpadc_probe_dt(pdev, indio_dev);
>> + else
>> + ret = sun4i_gpadc_probe_mfd(pdev, indio_dev);
>> +
>> + if (ret)
>> + return ret;
>> +
>> + pm_runtime_set_autosuspend_delay(&pdev->dev,
>> + SUN4I_GPADC_AUTOSUSPEND_DELAY);
>> + pm_runtime_use_autosuspend(&pdev->dev);
>> + pm_runtime_set_suspended(&pdev->dev);
>> + pm_runtime_enable(&pdev->dev);
>> +
>> ret = devm_iio_device_register(&pdev->dev, indio_dev);
>> if (ret < 0) {
>> dev_err(&pdev->dev, "could not register the device\n");
>> - goto err_map;
>> + goto err;
>> }
>>
>> return 0;
>>
>> -err_map:
>> - if (IS_ENABLED(CONFIG_THERMAL_OF))
>> - iio_map_array_unregister(indio_dev);
>> -
>> err:
>> + if (!info->use_dt && IS_ENABLED(CONFIG_THERMAL_OF))
>> + iio_map_array_unregister(indio_dev);
>> pm_runtime_put(&pdev->dev);
>> pm_runtime_disable(&pdev->dev);
>>
>> @@ -580,10 +682,11 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
>> static int sun4i_gpadc_remove(struct platform_device *pdev)
>> {
>> struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> + struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
>>
>> pm_runtime_put(&pdev->dev);
>> pm_runtime_disable(&pdev->dev);
>> - if (IS_ENABLED(CONFIG_THERMAL_OF))
>> + if (!info->use_dt && IS_ENABLED(CONFIG_THERMAL_OF))
>> iio_map_array_unregister(indio_dev);
>>
>> return 0;
>> @@ -599,6 +702,7 @@ static const struct platform_device_id sun4i_gpadc_id[] = {
>> static struct platform_driver sun4i_gpadc_driver = {
>> .driver = {
>> .name = "sun4i-gpadc-iio",
>> + .of_match_table = sun4i_gpadc_of_id,
>> .pm = &sun4i_gpadc_pm_ops,
>> },
>> .id_table = sun4i_gpadc_id,
>> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
>> index 509e736..139872c 100644
>> --- a/include/linux/mfd/sun4i-gpadc.h
>> +++ b/include/linux/mfd/sun4i-gpadc.h
>> @@ -38,6 +38,10 @@
>> #define SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(x) (GENMASK(3, 0) & BIT(x))
>> #define SUN6I_GPADC_CTRL1_ADC_CHAN_MASK GENMASK(3, 0)
>>
>> +/* TP_CTRL1 bits for sun8i SoCs */
>> +#define SUN8I_GPADC_CTRL1_CHOP_TEMP_EN BIT(8)
>> +#define SUN8I_GPADC_CTRL1_GPADC_CALI_EN BIT(7)
>> +
>> #define SUN4I_GPADC_CTRL2 0x08
>>
>> #define SUN4I_GPADC_CTRL2_TP_SENSITIVE_ADJUST(x) ((GENMASK(3, 0) & (x)) << 28)
>> --
>> 2.9.3
>>
>
> Thanks,
> Maxime
>
Thanks,
Quentin
--
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161220/6300bcb0/attachment.sig>
^ permalink raw reply
* [PATCH V6 05/10] acpi: apei: handle SEA notification type for ARMv8
From: James Morse @ 2016-12-20 15:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481147303-7979-6-git-send-email-tbaicar@codeaurora.org>
Hi Tyler,
On 07/12/16 21:48, Tyler Baicar wrote:
> ARM APEI extension proposal added SEA (Synchrounous External
> Abort) notification type for ARMv8.
> Add a new GHES error source handling function for SEA. If an error
> source's notification type is SEA, then this function can be registered
> into the SEA exception handler. That way GHES will parse and report
> SEA exceptions when they occur.
> diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
> index 2acbc60..66ab3fd 100644
> --- a/drivers/acpi/apei/ghes.c
> +++ b/drivers/acpi/apei/ghes.c
> @@ -767,6 +771,62 @@ static struct notifier_block ghes_notifier_sci = {
> .notifier_call = ghes_notify_sci,
> };
>
> +#ifdef CONFIG_HAVE_ACPI_APEI_SEA
> +static LIST_HEAD(ghes_sea);
> +
> +static int ghes_notify_sea(struct notifier_block *this,
> + unsigned long event, void *data)
> +{
> + struct ghes *ghes;
> + int ret = NOTIFY_DONE;
> +
> + rcu_read_lock();
> + list_for_each_entry_rcu(ghes, &ghes_sea, list) {
> + if (!ghes_proc(ghes))
> + ret = NOTIFY_OK;
> + }
> + rcu_read_unlock();
> +
> + return ret;
> +}
What stops this from being re-entrant?
ghes_copy_tofrom_phs() takes the ghes_ioremap_lock_irq spinlock, but there is
nothing to stop a subsequent instruction fetch or memory access causing another
(maybe different) Synchronous External Abort which deadlocks trying to take the
same lock.
ghes_notify_sea() looks to be based on ghes_notify_sci(), which (if I've found
the right part of the ACPI spec) is a level-low interrupt. spin_lock_irqsave()
would mask interrupts so there is no risk of a different notification firing on
the same CPU, (it looks like they are almost all ultimately an irq).
NMI is the odd one out because its not maskable like this, but ghes_notify_nmi()
has:
> if (!atomic_add_unless(&ghes_in_nmi, 1, 1))
> return ret;
To ensure there is only ever one thread poking around in this code.
What happens if a system describes two GHES sources, one using an irq the other
SEA? The SEA error can interrupt the irq error while its holding the above lock.
I guess this is also why all the NMI code in that file is separate.
Thanks,
James
^ permalink raw reply
* [PATCH] ARM: dts: imx28: Add simple-card support
From: Jörg Krause @ 2016-12-20 15:35 UTC (permalink / raw)
To: linux-arm-kernel
Add simple-card support to SAIF0 and SAIF1.
Signed-off-by: J?rg Krause <joerg.krause@embedded.rocks>
---
arch/arm/boot/dts/imx28.dtsi | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi
index afcf687..5783e26 100644
--- a/arch/arm/boot/dts/imx28.dtsi
+++ b/arch/arm/boot/dts/imx28.dtsi
@@ -1087,6 +1087,7 @@
};
saif0: saif at 80042000 {
+ #sound-dai-cells = <0>;
compatible = "fsl,imx28-saif";
reg = <0x80042000 0x2000>;
interrupts = <59>;
@@ -1104,6 +1105,7 @@
};
saif1: saif at 80046000 {
+ #sound-dai-cells = <0>;
compatible = "fsl,imx28-saif";
reg = <0x80046000 0x2000>;
interrupts = <58>;
--
2.10.2
^ permalink raw reply related
* [PATCH v2 2/3] net: dsa: mv88e6xxx: Add support for ethernet switch 88E6341/88E6141
From: Andrew Lunn @ 2016-12-20 15:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161220085138.3998-3-romain.perier@free-electrons.com>
On Tue, Dec 20, 2016 at 09:51:37AM +0100, Romain Perier wrote:
> The Marvell 88E6341 device is single-chip, 6-port ethernet switch with
> four integrated 10/100/1000Mbps ethernet transceivers and one high speed
> SerDes interfaces. It is compatible with switches of family 88E6352.
>
> This commit adds basic support for this switch by describing its
> capabilities to the driver.
>
> Signed-off-by: Romain Perier <romain.perier@free-electrons.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ 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