* [GIT PULL] i.MX fixes for 4.9
From: Fabio Estevam @ 2016-11-05 12:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOMZO5D9uBBwWV8UJOLj-2iwWO6sReB5BAtzdi1q6OqbtvcSOQ@mail.gmail.com>
Hi Olof,
On Mon, Oct 31, 2016 at 9:29 PM, Fabio Estevam <festevam@gmail.com> wrote:
> I think this will be fixed with this pcie-designware patch:
> http://www.spinics.net/lists/linux-pci/msg55244.html
Bjorn sent this fix to Linus, so we should be fine now.
>
>> Wandboard, has a peculiar MMC error. First I thought it was just the
>> card going bad, but now I'm not so sure since multi_v7 boots just fine
>> on the same hardware:
>
> Yes, I have been noticing this mmc error reported in your autobooter
> and I also thought it was some kind of a bad SD card.
>
> This particular error I have never been able to reproduce. Also
> checked kernelci and do not see this mmc error there.
>
> Interesting that it happens only with imx_v6_v7_defconfig and not with
> multi_v7_defconfig.
Today wandboard does not show the mmc error with imx_v6_v7_defconfig:
http://arm-soc.lixom.net/bootlogs/mainline/v4.9-rc3-376-gfb415f2/wandboard-arm-imx_v6_v7_defconfig.html
Looks like the mmc error come and go?
^ permalink raw reply
* [PATCH v7 0/2] Introducing Exynos ChipId driver
From: Pankaj Dubey @ 2016-11-05 12:03 UTC (permalink / raw)
To: linux-arm-kernel
Once again I am attempting this quite old patch series to introduce
Exynos Chipid driver.
Each Exynos SoC has ChipID block which can give information about SoC's
product Id and revision number.
This patch series introduces Exynos Chipid SoC driver. At the same time
it reduces dependency of mach-exynos files from plat-samsung, by removing
soc_is_exynosMMMM and samsung_rev API. Instead of it now we can use
soc_device_match API proposed by Arnd and getting discussed in thread [1].
Until now we are using static mapping of Exynos Chipid and using this static
mapping to know about SoC name and revision via soc_is_exynosMMMM macro. This
is quite cumbersome and every time new ARMv7 based Exynos SoC supports lands in
mainline a bunch of such new macros needs to be added. Quite long back during
support of Exynos5260 it has been discussed to solve this problem.
To solve this issue this patchset replaces use of soc_is_exynosMMMM by either
of_machine_is_compatible or soc_device_match depending upon usecase.
This patchset depends on [2], and I have prepared it on top of my other patchset [3]
(under-review) which introduces SCU device node for Exynos
I have tested this patch series on Exynos4210 based Origen board for normal SMP
boot.
[1]: https://patchwork.kernel.org/patch/9361389/
[2]: http://www.mail-archive.com/linux-kernel at vger.kernel.org/msg1261739.html
[3]: http://www.spinics.net/lists/arm-kernel/msg540498.html
Revision 6 and it's discussion can be found here:
- https://lkml.org/lkml/2016/5/25/101
Revision 5 and it's discussion can be found here:
- http://lists.infradead.org/pipermail/linux-arm-kernel/2014-December/310046.html
Revision 4 and it's discussion can be found here:
- https://lkml.org/lkml/2014/12/3/115
Chances since v6:
- Removed platform driver from chipid, instead use early_init to register soc_device
- Removed export functions from exynos chipid driver
- Replace soc_is_exynosMMMM via either of_machine_is_compatible or soc_device_match in
files in arm/mach-exynos folder
- This patchset depends on the following patch series by Geert Uytterhoeven. This series
includes patch which introduces soc_device_match.
http://www.mail-archive.com/linux-kernel at vger.kernel.org/msg1261739.html
- Rebased on latest krzk/for-next branch and retested.
Change since v5:
- Addressed Rob's review comments.
- Rebased on latest krzk/for-next branch and retested.
Changes since v4:
- Removed custom sysfs entries as they were not providing any new information
as pointed out by Arnd.
- Removed functions exporting product_id and revision, instead we will export
exynos_chipid_info structure. It will be helpfull when we need to provide more
fields of chipid outside of chipid, as commented by Yadwinder
- Converted all funcions as __init.
Change since v3:
- This patch set contains 5/6 and 6/6 patch from v3 series.
- Made EXYNOS_CHIPID config option non-user selectable,
as suggested by Tomasz Figa.
- Made uniform macro for EXYNOS4/5_SOC_MASK as EXYNOS_SOC_MASK as
suggested by Tomasz Figa.
- Made local variables static in chipid driver.
- Added existing SoC's product id's.
- Added platform driver support.
Changes since v2:
- Reorganized patches as suggested by Tomasz Figa.
- Addressed review comments of Tomasz Figa in i2c-s3c2410.c file.
Pankaj Dubey (2):
soc: samsung: add exynos chipid driver support
ARM: EXYNOS: refactoring of mach-exynos to enable chipid driver
arch/arm/mach-exynos/Kconfig | 1 +
arch/arm/mach-exynos/common.h | 92 -----------------
arch/arm/mach-exynos/exynos.c | 31 ------
arch/arm/mach-exynos/firmware.c | 10 +-
arch/arm/mach-exynos/include/mach/map.h | 21 ----
arch/arm/mach-exynos/platsmp.c | 22 ++--
arch/arm/mach-exynos/pm.c | 41 +++++---
arch/arm/plat-samsung/cpu.c | 14 ---
arch/arm/plat-samsung/include/plat/cpu.h | 2 -
arch/arm/plat-samsung/include/plat/map-s5p.h | 2 -
drivers/soc/samsung/Kconfig | 5 +
drivers/soc/samsung/Makefile | 1 +
drivers/soc/samsung/exynos-chipid.c | 148 +++++++++++++++++++++++++++
13 files changed, 201 insertions(+), 189 deletions(-)
delete mode 100644 arch/arm/mach-exynos/include/mach/map.h
create mode 100644 drivers/soc/samsung/exynos-chipid.c
--
2.7.4
^ permalink raw reply
* [PATCH v7 1/2] soc: samsung: add exynos chipid driver support
From: Pankaj Dubey @ 2016-11-05 12:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478347427-28409-1-git-send-email-pankaj.dubey@samsung.com>
Exynos SoCs have Chipid, for identification of product IDs
and SoC revisions. This patch intends to provide initialization
code for all these functionalities, at the same time it provides some
sysfs entries for accessing these information to user-space.
This driver uses existing binding for exynos-chipid.
CC: Grant Likely <grant.likely@linaro.org>
CC: Rob Herring <robh+dt@kernel.org>
CC: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
---
drivers/soc/samsung/Kconfig | 5 ++
drivers/soc/samsung/Makefile | 1 +
drivers/soc/samsung/exynos-chipid.c | 148 ++++++++++++++++++++++++++++++++++++
3 files changed, 154 insertions(+)
create mode 100644 drivers/soc/samsung/exynos-chipid.c
diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig
index 2455339..f9ab858 100644
--- a/drivers/soc/samsung/Kconfig
+++ b/drivers/soc/samsung/Kconfig
@@ -14,4 +14,9 @@ config EXYNOS_PM_DOMAINS
bool "Exynos PM domains" if COMPILE_TEST
depends on PM_GENERIC_DOMAINS || COMPILE_TEST
+config EXYNOS_CHIPID
+ bool "Exynos Chipid controller driver" if COMPILE_TEST
+ depends on (ARM && ARCH_EXYNOS) || ((ARM || ARM64) && COMPILE_TEST)
+ select SOC_BUS
+
endif
diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
index 3619f2e..2a8a85e 100644
--- a/drivers/soc/samsung/Makefile
+++ b/drivers/soc/samsung/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o exynos3250-pmu.o exynos4-pmu.o \
exynos5250-pmu.o exynos5420-pmu.o
obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o
+obj-$(CONFIG_EXYNOS_CHIPID) += exynos-chipid.o
diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c
new file mode 100644
index 0000000..9761e54
--- /dev/null
+++ b/drivers/soc/samsung/exynos-chipid.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * EXYNOS - CHIP ID support
+ * Author: Pankaj Dubey <pankaj.dubey@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+
+#define EXYNOS3250_SOC_ID 0xE3472000
+#define EXYNOS4210_SOC_ID 0x43210000
+#define EXYNOS4212_SOC_ID 0x43220000
+#define EXYNOS4412_SOC_ID 0xE4412000
+#define EXYNOS4415_SOC_ID 0xE4415000
+#define EXYNOS5250_SOC_ID 0x43520000
+#define EXYNOS5260_SOC_ID 0xE5260000
+#define EXYNOS5410_SOC_ID 0xE5410000
+#define EXYNOS5420_SOC_ID 0xE5420000
+#define EXYNOS5440_SOC_ID 0xE5440000
+#define EXYNOS5800_SOC_ID 0xE5422000
+
+#define EXYNOS_SOC_MASK 0xFFFFF000
+
+#define EXYNOS4210_REV_0 0x0
+#define EXYNOS4210_REV_1_0 0x10
+#define EXYNOS4210_REV_1_1 0x11
+
+#define EXYNOS_SUBREV_MASK (0xF << 4)
+#define EXYNOS_MAINREV_MASK (0xF << 0)
+#define EXYNOS_REV_MASK (EXYNOS_SUBREV_MASK | EXYNOS_MAINREV_MASK)
+
+
+static const char * __init product_id_to_soc_id(unsigned int product_id)
+{
+ const char *soc_name;
+ unsigned int soc_id = product_id & EXYNOS_SOC_MASK;
+
+ switch (soc_id) {
+ case EXYNOS3250_SOC_ID:
+ soc_name = "EXYNOS3250";
+ break;
+ case EXYNOS4210_SOC_ID:
+ soc_name = "EXYNOS4210";
+ break;
+ case EXYNOS4212_SOC_ID:
+ soc_name = "EXYNOS4212";
+ break;
+ case EXYNOS4412_SOC_ID:
+ soc_name = "EXYNOS4412";
+ break;
+ case EXYNOS4415_SOC_ID:
+ soc_name = "EXYNOS4415";
+ break;
+ case EXYNOS5250_SOC_ID:
+ soc_name = "EXYNOS5250";
+ break;
+ case EXYNOS5260_SOC_ID:
+ soc_name = "EXYNOS5260";
+ break;
+ case EXYNOS5420_SOC_ID:
+ soc_name = "EXYNOS5420";
+ break;
+ case EXYNOS5440_SOC_ID:
+ soc_name = "EXYNOS5440";
+ break;
+ case EXYNOS5800_SOC_ID:
+ soc_name = "EXYNOS5800";
+ break;
+ default:
+ soc_name = "UNKNOWN";
+ }
+ return soc_name;
+}
+
+static const struct of_device_id of_exynos_chipid_ids[] = {
+ {
+ .compatible = "samsung,exynos4210-chipid",
+ },
+ {},
+};
+
+/**
+ * exynos_chipid_early_init: Early chipid initialization
+ */
+int __init exynos_chipid_early_init(void)
+{
+ struct soc_device_attribute *soc_dev_attr;
+ struct soc_device *soc_dev;
+ struct device_node *root;
+ struct device_node *np;
+ void __iomem *exynos_chipid_base;
+ const struct of_device_id *match;
+ u32 product_id;
+ u32 revision;
+
+ np = of_find_matching_node_and_match(NULL,
+ of_exynos_chipid_ids, &match);
+ if (!np)
+ return -ENODEV;
+
+ exynos_chipid_base = of_iomap(np, 0);
+
+ if (!exynos_chipid_base)
+ return PTR_ERR(exynos_chipid_base);
+
+ product_id = __raw_readl(exynos_chipid_base);
+ revision = product_id & EXYNOS_REV_MASK;
+ iounmap(exynos_chipid_base);
+
+ soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+ if (!soc_dev_attr)
+ return -ENODEV;
+
+ soc_dev_attr->family = "Samsung Exynos";
+
+ root = of_find_node_by_path("/");
+ of_property_read_string(root, "model", &soc_dev_attr->machine);
+ of_node_put(root);
+
+ soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%x", revision);
+ soc_dev_attr->soc_id = product_id_to_soc_id(product_id);
+
+
+ pr_info("Exynos: CPU[%s] CPU_REV[0x%x] Detected\n",
+ product_id_to_soc_id(product_id), revision);
+
+ soc_dev = soc_device_register(soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ kfree(soc_dev_attr->revision);
+ kfree_const(soc_dev_attr->soc_id);
+ kfree(soc_dev_attr);
+ return PTR_ERR(soc_dev);
+ }
+
+ return 0;
+}
+early_initcall(exynos_chipid_early_init);
--
2.7.4
^ permalink raw reply related
* [PATCH v7 2/2] ARM: EXYNOS: refactoring of mach-exynos to enable chipid driver
From: Pankaj Dubey @ 2016-11-05 12:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478347427-28409-1-git-send-email-pankaj.dubey@samsung.com>
This patch enables chipid driver for ARCH_EXYNOS and refactors
machine code for using chipid driver for identification of
SoC ID and SoC rev.
Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
---
arch/arm/mach-exynos/Kconfig | 1 +
arch/arm/mach-exynos/common.h | 92 ----------------------------
arch/arm/mach-exynos/exynos.c | 31 ----------
arch/arm/mach-exynos/firmware.c | 10 +--
arch/arm/mach-exynos/include/mach/map.h | 21 -------
arch/arm/mach-exynos/platsmp.c | 22 ++++---
arch/arm/mach-exynos/pm.c | 41 ++++++++-----
arch/arm/plat-samsung/cpu.c | 14 -----
arch/arm/plat-samsung/include/plat/cpu.h | 2 -
arch/arm/plat-samsung/include/plat/map-s5p.h | 2 -
10 files changed, 47 insertions(+), 189 deletions(-)
delete mode 100644 arch/arm/mach-exynos/include/mach/map.h
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index b085855..a76c679 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -16,6 +16,7 @@ menuconfig ARCH_EXYNOS
select ARM_AMBA
select ARM_GIC
select COMMON_CLK_SAMSUNG
+ select EXYNOS_CHIPID
select EXYNOS_THERMAL
select EXYNOS_PMU
select EXYNOS_SROM
diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
index d19064b..9d76cf8 100644
--- a/arch/arm/mach-exynos/common.h
+++ b/arch/arm/mach-exynos/common.h
@@ -14,97 +14,6 @@
#include <linux/platform_data/cpuidle-exynos.h>
-#define EXYNOS3250_SOC_ID 0xE3472000
-#define EXYNOS3_SOC_MASK 0xFFFFF000
-
-#define EXYNOS4210_CPU_ID 0x43210000
-#define EXYNOS4212_CPU_ID 0x43220000
-#define EXYNOS4412_CPU_ID 0xE4412200
-#define EXYNOS4_CPU_MASK 0xFFFE0000
-
-#define EXYNOS5250_SOC_ID 0x43520000
-#define EXYNOS5410_SOC_ID 0xE5410000
-#define EXYNOS5420_SOC_ID 0xE5420000
-#define EXYNOS5440_SOC_ID 0xE5440000
-#define EXYNOS5800_SOC_ID 0xE5422000
-#define EXYNOS5_SOC_MASK 0xFFFFF000
-
-extern unsigned long samsung_cpu_id;
-
-#define IS_SAMSUNG_CPU(name, id, mask) \
-static inline int is_samsung_##name(void) \
-{ \
- return ((samsung_cpu_id & mask) == (id & mask)); \
-}
-
-IS_SAMSUNG_CPU(exynos3250, EXYNOS3250_SOC_ID, EXYNOS3_SOC_MASK)
-IS_SAMSUNG_CPU(exynos4210, EXYNOS4210_CPU_ID, EXYNOS4_CPU_MASK)
-IS_SAMSUNG_CPU(exynos4212, EXYNOS4212_CPU_ID, EXYNOS4_CPU_MASK)
-IS_SAMSUNG_CPU(exynos4412, EXYNOS4412_CPU_ID, EXYNOS4_CPU_MASK)
-IS_SAMSUNG_CPU(exynos5250, EXYNOS5250_SOC_ID, EXYNOS5_SOC_MASK)
-IS_SAMSUNG_CPU(exynos5410, EXYNOS5410_SOC_ID, EXYNOS5_SOC_MASK)
-IS_SAMSUNG_CPU(exynos5420, EXYNOS5420_SOC_ID, EXYNOS5_SOC_MASK)
-IS_SAMSUNG_CPU(exynos5440, EXYNOS5440_SOC_ID, EXYNOS5_SOC_MASK)
-IS_SAMSUNG_CPU(exynos5800, EXYNOS5800_SOC_ID, EXYNOS5_SOC_MASK)
-
-#if defined(CONFIG_SOC_EXYNOS3250)
-# define soc_is_exynos3250() is_samsung_exynos3250()
-#else
-# define soc_is_exynos3250() 0
-#endif
-
-#if defined(CONFIG_CPU_EXYNOS4210)
-# define soc_is_exynos4210() is_samsung_exynos4210()
-#else
-# define soc_is_exynos4210() 0
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS4212)
-# define soc_is_exynos4212() is_samsung_exynos4212()
-#else
-# define soc_is_exynos4212() 0
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS4412)
-# define soc_is_exynos4412() is_samsung_exynos4412()
-#else
-# define soc_is_exynos4412() 0
-#endif
-
-#define EXYNOS4210_REV_0 (0x0)
-#define EXYNOS4210_REV_1_0 (0x10)
-#define EXYNOS4210_REV_1_1 (0x11)
-
-#if defined(CONFIG_SOC_EXYNOS5250)
-# define soc_is_exynos5250() is_samsung_exynos5250()
-#else
-# define soc_is_exynos5250() 0
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS5410)
-# define soc_is_exynos5410() is_samsung_exynos5410()
-#else
-# define soc_is_exynos5410() 0
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS5420)
-# define soc_is_exynos5420() is_samsung_exynos5420()
-#else
-# define soc_is_exynos5420() 0
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS5440)
-# define soc_is_exynos5440() is_samsung_exynos5440()
-#else
-# define soc_is_exynos5440() 0
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS5800)
-# define soc_is_exynos5800() is_samsung_exynos5800()
-#else
-# define soc_is_exynos5800() 0
-#endif
-
extern u32 cp15_save_diag;
extern u32 cp15_save_power;
@@ -161,7 +70,6 @@ extern struct cpuidle_exynos_data cpuidle_coupled_exynos_data;
extern void exynos_set_delayed_reset_assertion(bool enable);
-extern unsigned int samsung_rev(void);
extern void exynos_core_restart(u32 core_id);
extern int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr);
extern int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr);
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
index fa08ef9..942131e 100644
--- a/arch/arm/mach-exynos/exynos.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -23,9 +23,6 @@
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
-#include <mach/map.h>
-#include <plat/cpu.h>
-
#include "common.h"
static struct platform_device exynos_cpuidle = {
@@ -67,37 +64,9 @@ static void __init exynos_init_late(void)
exynos_pm_init();
}
-static int __init exynos_fdt_map_chipid(unsigned long node, const char *uname,
- int depth, void *data)
-{
- struct map_desc iodesc;
- const __be32 *reg;
- int len;
-
- if (!of_flat_dt_is_compatible(node, "samsung,exynos4210-chipid") &&
- !of_flat_dt_is_compatible(node, "samsung,exynos5440-clock"))
- return 0;
-
- reg = of_get_flat_dt_prop(node, "reg", &len);
- if (reg == NULL || len != (sizeof(unsigned long) * 2))
- return 0;
-
- iodesc.pfn = __phys_to_pfn(be32_to_cpu(reg[0]));
- iodesc.length = be32_to_cpu(reg[1]) - 1;
- iodesc.virtual = (unsigned long)S5P_VA_CHIPID;
- iodesc.type = MT_DEVICE;
- iotable_init(&iodesc, 1);
- return 1;
-}
-
static void __init exynos_init_io(void)
{
debug_ll_io_init();
-
- of_scan_flat_dt(exynos_fdt_map_chipid, NULL);
-
- /* detect cpu id and rev. */
- s5p_init_cpu(S5P_VA_CHIPID);
}
/*
diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c
index fd6da54..a9f8504e 100644
--- a/arch/arm/mach-exynos/firmware.c
+++ b/arch/arm/mach-exynos/firmware.c
@@ -44,7 +44,7 @@ static int exynos_do_idle(unsigned long mode)
writel_relaxed(virt_to_phys(exynos_cpu_resume_ns),
sysram_ns_base_addr + 0x24);
writel_relaxed(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20);
- if (soc_is_exynos3250()) {
+ if (of_machine_is_compatible("samsung,exynos3250")) {
flush_cache_all();
exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE,
SMC_POWERSTATE_IDLE, 0);
@@ -65,7 +65,7 @@ static int exynos_cpu_boot(int cpu)
* Exynos3250 doesn't need to send smc command for secondary CPU boot
* because Exynos3250 removes WFE in secure mode.
*/
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250"))
return 0;
/*
@@ -73,7 +73,7 @@ static int exynos_cpu_boot(int cpu)
* But, Exynos4212 has only one secondary CPU so second parameter
* isn't used for informing secure firmware about CPU id.
*/
- if (soc_is_exynos4212())
+ if (of_machine_is_compatible("samsung,exynos4212"))
cpu = 0;
exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0);
@@ -94,7 +94,7 @@ static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
* additional offset for every CPU, with Exynos4412 being the only
* exception.
*/
- if (soc_is_exynos4412())
+ if (of_machine_is_compatible("samsung,exynos4412"))
boot_reg += 4 * cpu;
writel_relaxed(boot_addr, boot_reg);
@@ -110,7 +110,7 @@ static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
boot_reg = sysram_ns_base_addr + 0x1c;
- if (soc_is_exynos4412())
+ if (of_machine_is_compatible("samsung,exynos4412"))
boot_reg += 4 * cpu;
*boot_addr = readl_relaxed(boot_reg);
diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
deleted file mode 100644
index 0eef407..0000000
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com/
- *
- * EXYNOS - Memory map definitions
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#ifndef __ASM_ARCH_MAP_H
-#define __ASM_ARCH_MAP_H __FILE__
-
-#include <plat/map-base.h>
-
-#include <plat/map-s5p.h>
-
-#define EXYNOS_PA_CHIPID 0x10000000
-
-#endif /* __ASM_ARCH_MAP_H */
diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c
index 553d0d9..884e885 100644
--- a/arch/arm/mach-exynos/platsmp.c
+++ b/arch/arm/mach-exynos/platsmp.c
@@ -19,6 +19,7 @@
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/of_address.h>
+#include <linux/sys_soc.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>
#include <asm/cacheflush.h>
@@ -27,8 +28,6 @@
#include <asm/smp_scu.h>
#include <asm/firmware.h>
-#include <mach/map.h>
-
#include "common.h"
extern void exynos4_secondary_startup(void);
@@ -93,7 +92,8 @@ void exynos_cpu_power_down(int cpu)
{
u32 core_conf;
- if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) {
+ if (cpu == 0 && (of_machine_is_compatible("samsung,exynos5420") ||
+ of_machine_is_compatible("samsung,exynos5800"))) {
/*
* Bypass power down for CPU0 during suspend. Check for
* the SYS_PWR_REG value to decide if we are suspending
@@ -120,7 +120,7 @@ void exynos_cpu_power_up(int cpu)
{
u32 core_conf = S5P_CORE_LOCAL_PWR_EN;
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250"))
core_conf |= S5P_CORE_AUTOWAKEUP_EN;
pmu_raw_writel(core_conf,
@@ -168,9 +168,14 @@ int exynos_cluster_power_state(int cluster)
S5P_CORE_LOCAL_PWR_EN);
}
+static struct soc_device_attribute exynos4210_rev11[] = {
+ { .soc_id = "EXYNOS4210", .revision = "11", },
+ { },
+};
+
static void __iomem *cpu_boot_reg_base(void)
{
- if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
+ if (soc_device_match(exynos4210_rev11))
return pmu_base_addr + S5P_INFORM5;
return sysram_base_addr;
}
@@ -182,9 +187,10 @@ static inline void __iomem *cpu_boot_reg(int cpu)
boot_reg = cpu_boot_reg_base();
if (!boot_reg)
return IOMEM_ERR_PTR(-ENODEV);
- if (soc_is_exynos4412())
+ if (of_machine_is_compatible("samsung,exynos4412"))
boot_reg += 4*cpu;
- else if (soc_is_exynos5420() || soc_is_exynos5800())
+ else if (of_machine_is_compatible("samsung,exynos5420") ||
+ of_machine_is_compatible("samsung,exynos5800"))
boot_reg += 4;
return boot_reg;
}
@@ -356,7 +362,7 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
call_firmware_op(cpu_boot, core_id);
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250"))
dsb_sev();
else
arch_send_wakeup_ipi_mask(cpumask_of(cpu));
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c
index 60e6827..430b3e2 100644
--- a/arch/arm/mach-exynos/pm.c
+++ b/arch/arm/mach-exynos/pm.c
@@ -19,6 +19,7 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/sys_soc.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>
#include <linux/soc/samsung/exynos-pmu.h>
@@ -29,20 +30,30 @@
#include "common.h"
+static struct soc_device_attribute exynos4210_rev11[] = {
+ { .soc_id = "EXYNOS4210", .revision = "11", },
+ { },
+};
+
+static struct soc_device_attribute exynos4210_rev10[] = {
+ { .soc_id = "EXYNOS4210", .revision = "10", },
+ { },
+};
+
static inline void __iomem *exynos_boot_vector_addr(void)
{
- if (samsung_rev() == EXYNOS4210_REV_1_1)
+ if (soc_device_match(exynos4210_rev11))
return pmu_base_addr + S5P_INFORM7;
- else if (samsung_rev() == EXYNOS4210_REV_1_0)
+ else if (soc_device_match(exynos4210_rev10))
return sysram_base_addr + 0x24;
return pmu_base_addr + S5P_INFORM0;
}
static inline void __iomem *exynos_boot_vector_flag(void)
{
- if (samsung_rev() == EXYNOS4210_REV_1_1)
+ if (soc_device_match(exynos4210_rev11))
return pmu_base_addr + S5P_INFORM6;
- else if (samsung_rev() == EXYNOS4210_REV_1_0)
+ else if (soc_device_match(exynos4210_rev10))
return sysram_base_addr + 0x20;
return pmu_base_addr + S5P_INFORM1;
}
@@ -122,11 +133,13 @@ int exynos_pm_central_resume(void)
}
/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
-static void exynos_set_wakeupmask(long mask)
+static void exynos_set_wakeupmask(void)
{
- pmu_raw_writel(mask, S5P_WAKEUP_MASK);
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250")) {
+ pmu_raw_writel(0x40003ffe, S5P_WAKEUP_MASK);
pmu_raw_writel(0x0, S5P_WAKEUP_MASK2);
+ } else
+ pmu_raw_writel(0x0000ff3e, S5P_WAKEUP_MASK);
}
static void exynos_cpu_set_boot_vector(long flags)
@@ -140,7 +153,7 @@ static int exynos_aftr_finisher(unsigned long flags)
{
int ret;
- exynos_set_wakeupmask(soc_is_exynos3250() ? 0x40003ffe : 0x0000ff3e);
+ exynos_set_wakeupmask();
/* Set value of power down register for aftr mode */
exynos_sys_powerdown_conf(SYS_AFTR);
@@ -163,7 +176,7 @@ void exynos_enter_aftr(void)
cpu_pm_enter();
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250"))
exynos_set_boot_flag(cpuid, C2_STATE);
exynos_pm_central_suspend();
@@ -192,7 +205,7 @@ void exynos_enter_aftr(void)
exynos_pm_central_resume();
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250"))
exynos_clear_boot_flag(cpuid, C2_STATE);
cpu_pm_exit();
@@ -263,7 +276,7 @@ abort:
while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
cpu_relax();
- if (soc_is_exynos3250()) {
+ if (of_machine_is_compatible("samsung,exynos3250")) {
while (!pmu_raw_readl(S5P_PMU_SPARE2) &&
!atomic_read(&cpu1_wakeup))
cpu_relax();
@@ -285,7 +298,7 @@ abort:
call_firmware_op(cpu_boot, 1);
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250"))
dsb_sev();
else
arch_send_wakeup_ipi_mask(cpumask_of(1));
@@ -297,7 +310,7 @@ fail:
static int exynos_wfi_finisher(unsigned long flags)
{
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250"))
flush_cache_all();
cpu_do_idle();
@@ -319,7 +332,7 @@ static int exynos_cpu1_powerdown(void)
*/
exynos_cpu_power_down(1);
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250"))
pmu_raw_writel(0, S5P_PMU_SPARE2);
ret = cpu_suspend(0, exynos_wfi_finisher);
diff --git a/arch/arm/plat-samsung/cpu.c b/arch/arm/plat-samsung/cpu.c
index a107b3a..e58f0f6 100644
--- a/arch/arm/plat-samsung/cpu.c
+++ b/arch/arm/plat-samsung/cpu.c
@@ -21,12 +21,6 @@
unsigned long samsung_cpu_id;
static unsigned int samsung_cpu_rev;
-unsigned int samsung_rev(void)
-{
- return samsung_cpu_rev;
-}
-EXPORT_SYMBOL(samsung_rev);
-
void __init s3c64xx_init_cpu(void)
{
samsung_cpu_id = readl_relaxed(S3C_VA_SYS + 0x118);
@@ -43,11 +37,3 @@ void __init s3c64xx_init_cpu(void)
pr_info("Samsung CPU ID: 0x%08lx\n", samsung_cpu_id);
}
-
-void __init s5p_init_cpu(const void __iomem *cpuid_addr)
-{
- samsung_cpu_id = readl_relaxed(cpuid_addr);
- samsung_cpu_rev = samsung_cpu_id & 0xFF;
-
- pr_info("Samsung CPU ID: 0x%08lx\n", samsung_cpu_id);
-}
diff --git a/arch/arm/plat-samsung/include/plat/cpu.h b/arch/arm/plat-samsung/include/plat/cpu.h
index b7b702a..913c176 100644
--- a/arch/arm/plat-samsung/include/plat/cpu.h
+++ b/arch/arm/plat-samsung/include/plat/cpu.h
@@ -115,8 +115,6 @@ extern void s3c24xx_init_io(struct map_desc *mach_desc, int size);
extern void s3c64xx_init_cpu(void);
extern void s5p_init_cpu(const void __iomem *cpuid_addr);
-extern unsigned int samsung_rev(void);
-
extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no);
extern void s3c24xx_init_clocks(int xtal);
diff --git a/arch/arm/plat-samsung/include/plat/map-s5p.h b/arch/arm/plat-samsung/include/plat/map-s5p.h
index 512ed1f..d6853f1 100644
--- a/arch/arm/plat-samsung/include/plat/map-s5p.h
+++ b/arch/arm/plat-samsung/include/plat/map-s5p.h
@@ -13,8 +13,6 @@
#ifndef __ASM_PLAT_MAP_S5P_H
#define __ASM_PLAT_MAP_S5P_H __FILE__
-#define S5P_VA_CHIPID S3C_ADDR(0x02000000)
-
#define VA_VIC(x) (S3C_VA_IRQ + ((x) * 0x10000))
#define VA_VIC0 VA_VIC(0)
#define VA_VIC1 VA_VIC(1)
--
2.7.4
^ permalink raw reply related
* Low network throughput on i.MX28
From: Jörg Krause @ 2016-11-05 12:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <963717394.159124.9867e3e7-5710-4844-a098-6f44bd852a6d.open-xchange@email.1und1.de>
Hello Vinod,
as recommanded by Stefan Wahren I'm turning on you about this issue.
Please see below...
On Sat, 2016-11-05 at 12:33 +0100, Stefan Wahren wrote:
> Hi J?rg,
>
> > J?rg Krause <joerg.krause@embedded.rocks> hat am 4. November 2016
> > um 23:42
> > geschrieben:
> >
> >
> > Hi Stefan,
> >
> > sorry, I forget the link in the previous mail.
> >
> > On Fri, 2016-11-04 at 20:30 +0100, Stefan Wahren wrote:
> > > Hi J?rg,
> > >
> > > > J?rg Krause <joerg.krause@embedded.rocks> hat am 4. November
> > > > 2016
> > > > um 19:44
> > > > geschrieben:
> > > >
> > > >
> > > > Hi Shawn,
> > > >
> > > > On Wed, 2016-11-02 at 09:24 +0100, Stefan Wahren wrote:
> > > > > Am 02.11.2016 um 09:14 schrieb J?rg Krause:
> > > > > > On Sat, 2016-10-29 at 11:08 +0200, Stefan Wahren wrote:
> > > > > > > > J?rg Krause <joerg.krause@embedded.rocks> hat am 29.
> > > > > > > > Oktober
> > > > > > > > 2016
> > > > > > > > um 01:07
> > > > > > > > geschrieben:
> > > > > > > >
> > > > > > > >
> > > > > > > > You mentioned [1] an optimization in the Freescale
> > > > > > > > vendor
> > > > > > > > Linux
> > > > > > > > kernel
> > > > > > > > [2]. I would really like to see this optimization in
> > > > > > > > the
> > > > > > > > mainline
> > > > > > > > kernel.
> > > > > > > >
> > > > > > > > Did you ever tried to port this code from Freescale to
> > > > > > > > mainline?
> > > > > > >
> > > > > > > Yes, i tried once but i was frustrated soon because of
> > > > > > > the
> > > > > > > lot of
> > > > > > > required
> > > > > > > changes and resulting issues.
> > > > > >
> > > > > > I got the PIO mode working for the mxs-mmc driver. For this
> > > > > > I
> > > > > > ported
> > > > > > the PIO code from the vendor kernel and removed the usage
> > > > > > of
> > > > > > the
> > > > > > DMA
> > > > > > engine entirely.
> > > > >
> > > > > Good job
> > > > >
> > > > > >
> > > > > > Testing network bandwidth with iperf, I get about
> > > > > > ~10Mbit/sec
> > > > > > with
> > > > > > PIO
> > > > > > mode compared to ~6.5Mbit/sec with DMA mode for UDP and
> > > > > > about
> > > > > > ~6.5Mbit/sec compared to ~4.5Mbit/sec with DMA mode for
> > > > > > TCP.
> > > > >
> > > > > And how about MMC / sd card performance?
> > > >
> > > > I noticed poor performance with the i.MX28 MMC and/or DMA
> > > > driver
> > > > using
> > > > the mainline kernel compared to the vendor Freescale kernel
> > > > 2.6.35.
> > > > I've seen that hou have added the drivers to mainline some
> > > > years
> > > > ago.
> > > >
> > > > My custom i.MX28 board has a wifi chip attached to the SSP2
> > > > interface.
> > > > Comparing the bandwith with iperf I get >20Mbits/sec on the
> > > > vendor
> > > > kernel and <5Mbits/sec on the mainline kernel.
> > >
> > > there is one thing about the clock handling. I noticed that the
> > > Vendor Kernel
> > > round up the clock frequency and the Mainline Kernel round down
> > > the
> > > clock
> > > frequency [1]. So don't trust the clock ratings from DT / board
> > > code.
> > > Better
> > > verify the register settings or check it with an osci.
> > >
> > > [1] - http://www.spinics.net/lists/linux-mmc/msg09132.html
> >
> > I checked the clock rate setting by reading the register 0x80014070
> > (HW_SSP2_TIMING). CLOCK_DIVIDE is 0x2 and CLOCK_RATE is 0x0. As SSP
> > CLK
> > is 96MHz this makes a clock rate of 48MHz.
> >
> > There was a discussion on the mailing list [1] about that tasklets
> > might be slow.
> >
> > [1] http://lists.infradead.org/pipermail/linux-arm-kernel/2011-Febr
> > uary
> > /043395.html
>
> if i unterstand it right the tasklet is not the problem, but the
> design of the
> MXS DMA driver. Please refer to the chapter "General Design Notes" to
> the
> documentation of the DMA provider [2].
> I think the MXS DMA driver is affected. Maybe you should ask Vinod
> Koul about
> this.
>
> [2] - https://www.kernel.org/doc/Documentation/dmaengine/provider.txt
@ Vinod
In short, I noticed poor performance in the SSP2 (MMC/SD/SDIO)
interface on a custom i.MX28 board with a wifi chip attached. Comparing
the bandwith with iperf I get >20Mbits/sec on the vendor kernel and
<5Mbits/sec on the mainline kernel. I am trying to investigate what the
bottleneck is.
@ Stefan, all
My understanding is that the tasklet in this case is responsible for
reading the response registers of the DMA controller and return the
response to the MMC host driver.
The vendor kernel does this in the interrupt routine of mxs-mmc by
issueing a complete whereas the mainline kernel does this in the
interrupt routine in mxs-dma by scheduling the tasklet.
To check if this makes any difference I replaced the tasklet() usage
with using the complete() infrastructure. For this I hacked the DMA
engine and the MXS DMA driver. However, the performance stays the same.
So, if I understand correctly, this is not an issue here, right? So if
not the tasklet, what do you suspect?
J?rg
^ permalink raw reply
* Low network throughput on i.MX28
From: Koul, Vinod @ 2016-11-05 12:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478347610.353.2.camel@embedded.rocks>
On Sat, 2016-11-05 at 13:06 +0100, J?rg Krause wrote:
> @ Vinod
> In short, I noticed poor performance in the SSP2 (MMC/SD/SDIO)
> interface on a custom i.MX28 board with a wifi chip attached.
> Comparing
> the bandwith with iperf I get >20Mbits/sec on the vendor kernel and
> <5Mbits/sec on the mainline kernel. I am trying to investigate what
> the
> bottleneck is.
is this imx-dma or imx-sdma..
>
> @ Stefan, all
> My understanding is that the tasklet in this case is responsible for
> reading the response registers of the DMA controller and return the
> response to the MMC host driver.
>
> The vendor kernel does this in the interrupt routine of mxs-mmc by
> issueing a complete whereas the mainline kernel does this in the
> interrupt routine in mxs-dma by scheduling the tasklet.
Is vendor kernel using dmaengine APIs or not?
Okay, if we talk about getting best performance, I always advise folks
to issue next transaction in the interrupt routine and then do
descriptor management and callback in tasklet.
Some drivers do that correctly but some don't..
Tasklet can be an issue but only if there is a huge scheduling delay for
the tasklet. You can check using tracing tools and confirm.
>
> To check if this makes any difference I replaced the tasklet() usage
> with using the complete() infrastructure. For this I hacked the DMA
> engine and the MXS DMA driver. However, the performance stays the
> same.
>
> So, if I understand correctly, this is not an issue here, right? So if
> not the tasklet, what do you suspect?
--
~Vinod
^ permalink raw reply
* Low network throughput on i.MX28
From: Jörg Krause @ 2016-11-05 12:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478349578.3405.5.camel@intel.com>
On Sat, 2016-11-05 at 12:39 +0000, Koul, Vinod wrote:
> On Sat, 2016-11-05 at 13:06 +0100, J?rg Krause wrote:
> > @ Vinod
> > In short, I noticed poor performance in the SSP2 (MMC/SD/SDIO)
> > interface on a custom i.MX28 board with a wifi chip attached.
> > Comparing
> > the bandwith with iperf I get >20Mbits/sec on the vendor kernel and
> > <5Mbits/sec on the mainline kernel. I am trying to investigate what
> > the
> > bottleneck is.
>
> is this imx-dma or imx-sdma..
Its' mxs-dma.
>
> >
> > @ Stefan, all
> > My understanding is that the tasklet in this case is responsible
> > for
> > reading the response registers of the DMA controller and return the
> > response to the MMC host driver.
> >
> > The vendor kernel does this in the interrupt routine of mxs-mmc by
> > issueing a complete whereas the mainline kernel does this in the
> > interrupt routine in mxs-dma by scheduling the tasklet.
>
> Is vendor kernel using dmaengine APIs or not?
No. It's using a custom dmaengine.
>
> Okay, if we talk about getting best performance, I always advise
> folks
> to issue next transaction in the interrupt routine and then do
> descriptor management and callback in tasklet.
>
> Some drivers do that correctly but some don't..
Do you have an example for a driver doing it correctly?
> Tasklet can be an issue but only if there is a huge scheduling delay
> for
> the tasklet. You can check using tracing tools and confirm.
Don't think the tasklets is an issue here as I replaced the tasklets in
the dmaengine API by completion (which the vendor kernel uses) and
there are no performance benefits. However, I am not a Linux kernel
developer...
J?rg
^ permalink raw reply
* Low network throughput on i.MX28
From: Fabio Estevam @ 2016-11-05 12:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478349578.3405.5.camel@intel.com>
Hi Vinod,
On Sat, Nov 5, 2016 at 10:39 AM, Koul, Vinod <vinod.koul@intel.com> wrote:
> On Sat, 2016-11-05 at 13:06 +0100, J?rg Krause wrote:
>> @ Vinod
>> In short, I noticed poor performance in the SSP2 (MMC/SD/SDIO)
>> interface on a custom i.MX28 board with a wifi chip attached.
>> Comparing
>> the bandwith with iperf I get >20Mbits/sec on the vendor kernel and
>> <5Mbits/sec on the mainline kernel. I am trying to investigate what
>> the
>> bottleneck is.
>
> is this imx-dma or imx-sdma..
This is drivers/dma/mxs-dma.c, thanks.
^ permalink raw reply
* [Bug] ARM: mxs: STI: console can't wake up from freeze
From: Daniel Lezcano @ 2016-11-05 13:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <122295766.159553.9867e3e7-5710-4844-a098-6f44bd852a6d.open-xchange@email.1und1.de>
On Sat, Nov 05, 2016 at 01:01:26PM +0100, Stefan Wahren wrote:
[?... ]
> > > CONFIG_SUSPEND=y
> > > CONFIG_SUSPEND_FREEZER=y
> > > CONFIG_PM=y
> > > CONFIG_CPU_IDLE is not set
> >
> > hmmm, why not have CONFIG_CPU_IDLE set?
>
> I'm using mxs_defconfig which doesn't select the ARM CPU idle. Is this
> necessary?
Very likely :) suspend_freezer and cpuidle are tied together.
Moreover, while reading the code, it appears without CONFIG_CPU_IDLE the
function cpuidle_idle_call is constantly failing, ending up by having the
cpu looping again and again in the idle loop. The function stubs return
-ENODEV in the cpuidle's header when CONFIG_CPU_IDLE is not set.
If I'm not wrong the traces should show the cpu actually does never go
to suspend. As soon as it enters the state, it should exit immediately
without an interrupt event.
Probably there is an inconsistent configuration leaving the kernel with
a strange wakeup condition or a race with the short the suspend/resume
cycle delay.
First test would be to enable CONFIG_CPU_IDLE.
If it is confirmed, it would be nice to feed bugzilla.
^ permalink raw reply
* Low network throughput on i.MX28
From: Jörg Krause @ 2016-11-05 13:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478349578.3405.5.camel@intel.com>
On Sat, 2016-11-05 at 12:39 +0000, Koul, Vinod wrote:
> On Sat, 2016-11-05 at 13:06 +0100, J?rg Krause wrote:
> > @ Vinod
> > In short, I noticed poor performance in the SSP2 (MMC/SD/SDIO)
> > interface on a custom i.MX28 board with a wifi chip attached.
> > Comparing
> > the bandwith with iperf I get >20Mbits/sec on the vendor kernel and
> > <5Mbits/sec on the mainline kernel. I am trying to investigate what
> > the
> > bottleneck is.
>
> is this imx-dma or imx-sdma..
>
> >
> > @ Stefan, all
> > My understanding is that the tasklet in this case is responsible
> > for
> > reading the response registers of the DMA controller and return the
> > response to the MMC host driver.
> >
> > The vendor kernel does this in the interrupt routine of mxs-mmc by
> > issueing a complete whereas the mainline kernel does this in the
> > interrupt routine in mxs-dma by scheduling the tasklet.
>
> Is vendor kernel using dmaengine APIs or not?
It's this engine [1].
[1] http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/tree/ar
ch/arm/plat-mxs/dmaengine.c?h=imx_2.6.35_1.1.0
^ permalink raw reply
* [PATCH 1/1] pinctrl: st: st_pinconf_dbg_show wrong format string
From: Heinrich Schuchardt @ 2016-11-05 14:25 UTC (permalink / raw)
To: linux-arm-kernel
function is defined as unsigned int.
So we need %u to print it.
Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
---
drivers/pinctrl/pinctrl-st.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c
index b7bb371..8308f15 100644
--- a/drivers/pinctrl/pinctrl-st.c
+++ b/drivers/pinctrl/pinctrl-st.c
@@ -1006,7 +1006,7 @@ static void st_pinconf_dbg_show(struct pinctrl_dev *pctldev,
function = st_pctl_get_pin_function(pc, offset);
if (function)
- snprintf(f, 10, "Alt Fn %d", function);
+ snprintf(f, 10, "Alt Fn %u", function);
else
snprintf(f, 5, "GPIO");
--
2.10.1
^ permalink raw reply related
* [PATCH 1/1] pinctrl: st: st_pctl_dt_parse_groups simplify expression
From: Heinrich Schuchardt @ 2016-11-05 14:35 UTC (permalink / raw)
To: linux-arm-kernel
for_each_property_of_node(pins, pp) checks that pp is not NULL.
So there is no need to check it inside the loop.
Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
---
drivers/pinctrl/pinctrl-st.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c
index 8308f15..676efcc 100644
--- a/drivers/pinctrl/pinctrl-st.c
+++ b/drivers/pinctrl/pinctrl-st.c
@@ -1181,7 +1181,7 @@ static int st_pctl_dt_parse_groups(struct device_node *np,
if (!strcmp(pp->name, "name"))
continue;
- if (pp && (pp->length/sizeof(__be32)) >= OF_GPIO_ARGS_MIN) {
+ if (pp->length / sizeof(__be32) >= OF_GPIO_ARGS_MIN) {
npins++;
} else {
pr_warn("Invalid st,pins in %s node\n", np->name);
--
2.10.1
^ permalink raw reply related
* [PATCH 1/2] arm64: dts: add USB1-related nodes of Allwinner A64
From: Icenowy Zheng @ 2016-11-05 14:38 UTC (permalink / raw)
To: linux-arm-kernel
Allwinner A64 have two HCI USB controllers, a OTG controller and a USB
PHY device which have two ports. One of the port is wired to both a HCI
USB controller and the OTG controller, which is currently not supported.
The another one is only wired to a HCI controller, and the device node of
OHCI/EHCI controller of the port can be added now.
Also the A64 USB PHY device node is also added for the HCI controllers to
work.
Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 50 +++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 3d70be3..c2b6dc8 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -259,5 +259,55 @@
interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
};
+
+ usbphy: phy at 01c19400 {
+ compatible = "allwinner,sun50i-a64-usb-phy";
+ reg = <0x01c19400 0x14>,
+ <0x01c1b800 0x4>;
+ reg-names = "phy_ctrl",
+ "pmu1";
+ clocks = <&ccu CLK_USB_PHY0>,
+ <&ccu CLK_USB_PHY1>;
+ clock-names = "usb0_phy",
+ "usb1_phy";
+ resets = <&ccu RST_USB_PHY0>,
+ <&ccu RST_USB_PHY1>;
+ reset-names = "usb0_reset",
+ "usb1_reset";
+ status = "disabled";
+ #phy-cells = <1>;
+ };
+
+ ohci1: usb at 01c1a400 {
+ compatible = "allwinner,sun50i-a64-ohci", "generic-ohci";
+ reg = <0x01c1b400 0x100>;
+ interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
+ /*
+ * According to the user manual, OHCI1 USB clock
+ * depends on OHCI0 clock.
+ */
+ clocks = <&ccu CLK_BUS_OHCI1>,
+ <&ccu CLK_USB_OHCI0>,
+ <&ccu CLK_USB_OHCI1>;
+ resets = <&ccu RST_BUS_OHCI1>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ehci1: usb at 01c1a000 {
+ compatible = "allwinner,sun50i-a64-ehci", "generic-ehci";
+ reg = <0x01c1b000 0x100>;
+ interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_OHCI1>,
+ <&ccu CLK_BUS_EHCI1>,
+ <&ccu CLK_USB_OHCI0>,
+ <&ccu CLK_USB_OHCI1>;
+ resets = <&ccu RST_BUS_OHCI1>,
+ <&ccu RST_BUS_EHCI1>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
};
};
--
2.10.1
^ permalink raw reply related
* [PATCH 2/2] arm64: dts: sunxi: enable EHCI1, OHCI1 and USB PHY nodes in Pine64
From: Icenowy Zheng @ 2016-11-05 14:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161105143830.10099-1-icenowy@aosc.xyz>
Pine64 have two USB Type-A ports, which are wired to the two ports of
A64 USB PHY, and the lower port is the EHCI/OHCI1 port.
Enable the necessary nodes to enable the lower USB port to work.
Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
index 4709590..d836995 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
@@ -72,3 +72,15 @@
&i2c1_pins {
bias-pull-up;
};
+
+&usbphy {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
--
2.10.1
^ permalink raw reply related
* [GIT PULL] ARM: dts: uniphier: UniPhier DT updates for v4.10
From: Masahiro Yamada @ 2016-11-05 15:18 UTC (permalink / raw)
To: linux-arm-kernel
Hi Arnd, Olof,
Here are UniPhier DT (32bit) updates for the v4.10 merge window.
Please pull!
(This time, I based this pull-request on -rc3 instead of -rc1
because I want to make sure to have no conflict
between fixes that had already been pulled-in and
new development commits in this series.)
The following changes since commit a909d3e636995ba7c349e2ca5dbb528154d4ac30:
Linux 4.9-rc3 (2016-10-29 13:52:02 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-uniphier.git
tags/uniphier-dt-v4.10
for you to fetch changes up to 13b4a6190b25c7bf69239bbc212d256656ada7b9:
ARM: dts: uniphier: make compatible of syscon nodes SoC-specific
(2016-11-05 23:33:36 +0900)
----------------------------------------------------------------
UniPhier ARM SoC DT updates for v4.10
- Add OPP tables to support generic cpufreq driver
- Use more clocks/resets properties
- Misc fixes and cleanups
----------------------------------------------------------------
Masahiro Yamada (7):
ARM: dts: uniphier: increase register region size of sysctrl node
ARM: dts: uniphier: add CPU clocks and OPP table for Pro5 SoC
ARM: dts: uniphier: add CPU clocks and OPP table for PXs2 SoC
ARM: dts: uniphier: make 32bit SoC DTSI linear
ARM: dts: uniphier: remove redundant serial fifo-size properties
ARM: dts: uniphier: add clocks/resets to EHCI nodes of sLD3 SoC
ARM: dts: uniphier: make compatible of syscon nodes SoC-specific
arch/arm/boot/dts/uniphier-common32.dtsi | 199 --------------
arch/arm/boot/dts/uniphier-ld4.dtsi | 358 +++++++++++++++++--------
arch/arm/boot/dts/uniphier-pro4.dtsi | 378 +++++++++++++++++---------
arch/arm/boot/dts/uniphier-pro5.dtsi | 432 ++++++++++++++++++++++--------
arch/arm/boot/dts/uniphier-pxs2.dtsi | 399 +++++++++++++++++++--------
arch/arm/boot/dts/uniphier-sld3.dtsi | 21 +-
arch/arm/boot/dts/uniphier-sld8.dtsi | 359 +++++++++++++++++--------
7 files changed, 1344 insertions(+), 802 deletions(-)
delete mode 100644 arch/arm/boot/dts/uniphier-common32.dtsi
--
Best Regards
Masahiro Yamada
^ permalink raw reply
* [GIT PULL] arm64: dts: uniphier: UniPhier DT updates (64bit) for v4.10
From: Masahiro Yamada @ 2016-11-05 15:25 UTC (permalink / raw)
To: linux-arm-kernel
Hi Arnd, Olof,
Here are UniPhier DT (64bit) updates for the v4.10 merge window.
Please pull!
The following changes since commit a909d3e636995ba7c349e2ca5dbb528154d4ac30:
Linux 4.9-rc3 (2016-10-29 13:52:02 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-uniphier.git
tags/uniphier-dt64-v4.10
for you to fetch changes up to fb28cef06a9065748fbc6949dd7fca3c320c8e0d:
arm64: dts: uniphier: make compatible of syscon nodes SoC-specific
(2016-11-05 23:30:11 +0900)
----------------------------------------------------------------
UniPhier ARM64 SoC DT updates for v4.10
- Switch CPU enable-method from spin-table to PSCI
- Add OPP tables to support generic cpufreq driver
- Misc fixes
----------------------------------------------------------------
Masahiro Yamada (5):
arm64: dts: uniphier: switch over to PSCI enable method
arm64: dts: uniphier: increase register region size of sysctrl node
arm64: dts: uniphier: add CPU clock and OPP table for LD11 SoC
arm64: dts: uniphier: add CPU clocks and OPP tables for LD20 SoC
arm64: dts: uniphier: make compatible of syscon nodes SoC-specific
arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi | 59 ++++++++++--
arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi | 111 +++++++++++++++++++---
2 files changed, 148 insertions(+), 22 deletions(-)
--
Best Regards
Masahiro Yamada
^ permalink raw reply
* [Bug] ARM: mxs: STI: console can't wake up from freeze
From: Stefan Wahren @ 2016-11-05 15:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161105130034.GG15759@mai>
Hi Daniel,
> Daniel Lezcano <daniel.lezcano@linaro.org> hat am 5. November 2016 um 14:00
> geschrieben:
>
>
> On Sat, Nov 05, 2016 at 01:01:26PM +0100, Stefan Wahren wrote:
>
> [?... ]
>
> > > > CONFIG_SUSPEND=y
> > > > CONFIG_SUSPEND_FREEZER=y
> > > > CONFIG_PM=y
> > > > CONFIG_CPU_IDLE is not set
> > >
> > > hmmm, why not have CONFIG_CPU_IDLE set?
> >
> > I'm using mxs_defconfig which doesn't select the ARM CPU idle. Is this
> > necessary?
>
> Very likely :) suspend_freezer and cpuidle are tied together.
>
> Moreover, while reading the code, it appears without CONFIG_CPU_IDLE the
> function cpuidle_idle_call is constantly failing, ending up by having the
> cpu looping again and again in the idle loop. The function stubs return
> -ENODEV in the cpuidle's header when CONFIG_CPU_IDLE is not set.
>
> If I'm not wrong the traces should show the cpu actually does never go
> to suspend. As soon as it enters the state, it should exit immediately
> without an interrupt event.
>
> Probably there is an inconsistent configuration leaving the kernel with
> a strange wakeup condition or a race with the short the suspend/resume
> cycle delay.
>
> First test would be to enable CONFIG_CPU_IDLE.
>
> If it is confirmed, it would be nice to feed bugzilla.
first i tried with CONFIG_CPU_IDLE=Y and then additionally with
CONFIG_ARM_CPUIDLE=y.
In both cases the issue is still reproducible.
As i wrote in my email before, i added a pr_info() into freeze_wake. But i never
see the output of this message. So i assume freeze_wake is never called. Again,
how could this happen?
^ permalink raw reply
* [PATCH 1/4] ARM: EXYNOS: Remove smp_init_cpus hook from platsmp.c
From: Krzysztof Kozlowski @ 2016-11-05 15:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478230764-13748-2-git-send-email-pankaj.dubey@samsung.com>
On Fri, Nov 04, 2016 at 09:09:21AM +0530, Pankaj Dubey wrote:
> We can safely remove exynos_smp_init_cpus() hook from mach-exynos/platsmp.c,
> as all SMP platforms in mach-exynos can rely on DT for CPU core description
> instead of determining number of cores from the SCU.
>
> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
> ---
> arch/arm/mach-exynos/platsmp.c | 31 -------------------------------
> 1 file changed, 31 deletions(-)
>
Thanks, applied. Somehow your patchset did not reach linux-samsung-soc's
Parchwork. I don't have a clue why... It is on linux-arm-kernel's
Patchwork.
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH 2/4] ARM: dts: exynos: Add SCU device node to exynos4.dtsi
From: Krzysztof Kozlowski @ 2016-11-05 15:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478230764-13748-3-git-send-email-pankaj.dubey@samsung.com>
On Fri, Nov 04, 2016 at 09:09:22AM +0530, Pankaj Dubey wrote:
> Exynos4 like other Cortex-A9 SoC's has a Snoop Control Unit(SCU)
> and its SFR are used during SMP boot and S2R. Add SCU node to the device tree.
>
> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
> ---
> arch/arm/boot/dts/exynos4.dtsi | 5 +++++
> 1 file changed, 5 insertions(+)
>
Thanks, applied.
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH v4] ARM: mmp: let clk_disable() return immediately if clk is NULL
From: Masahiro Yamada @ 2016-11-05 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474221526-21195-1-git-send-email-yamada.masahiro@socionext.com>
Hi Eric Miao, Haojian Zhuang,
Could you pick up this patch, please?
2016-09-19 2:58 GMT+09:00 Masahiro Yamada <yamada.masahiro@socionext.com>:
> In many of clk_disable() implementations, it is a no-op for a NULL
> pointer input, but this is one of the exceptions.
>
> Making it treewide consistent will allow clock consumers to call
> clk_disable() without NULL pointer check.
>
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> ---
>
> Changes in v4:
> - Split into per-arch patches
>
> Changes in v3:
> - Return only when clk is NULL. Do not take care of error pointer.
>
> Changes in v2:
> - Rebase on Linux 4.6-rc1
>
> arch/arm/mach-mmp/clock.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/arch/arm/mach-mmp/clock.c b/arch/arm/mach-mmp/clock.c
> index ac6633d..28fe64c 100644
> --- a/arch/arm/mach-mmp/clock.c
> +++ b/arch/arm/mach-mmp/clock.c
> @@ -67,6 +67,9 @@ void clk_disable(struct clk *clk)
> {
> unsigned long flags;
>
> + if (!clk)
> + return;
> +
> WARN_ON(clk->enabled == 0);
>
> spin_lock_irqsave(&clocks_lock, flags);
> --
> 1.9.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--
Best Regards
Masahiro Yamada
^ permalink raw reply
* [PATCH 02/10] iio: adc: Add stm32 support
From: Jonathan Cameron @ 2016-11-05 15:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4687bde5-1e1e-83bc-0aca-3224399f0fa2@st.com>
On 03/11/16 08:20, Fabrice Gasnier wrote:
> On 10/30/2016 04:27 PM, Jonathan Cameron wrote:
>> On 25/10/16 17:25, Fabrice Gasnier wrote:
>>> This patch adds support for STMicroelectronics STM32 MCU's analog to
>>> digital converter.
>>>
>>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
>> Hi Fabrice,
>>
>> Sometimes I hate SoC ADCs. For some reason the hardware designers seem to
>> try and throw everything and the kitchen sink at them. Discontinuous mode
>> as an example in this device. Not seen that particular piece of fun before
>> and glad to see you haven't 'yet' tried to support it!
>>
>> Anyhow, the complexity of the hardware leads to an initially complex driver.
>> My first thought it that this would be easier to follow / review if we
>> built it up in smaller steps. Perhaps ditch the injected channel support
>> entirely in the first instance. I also wonder if you don't need to support
>> that whole thing (injected sampling) as another iio device entirely using the
>> same channels. That's kind of what it is from a data flow point of view
>> (we've had arbitary sequencers before with priorities - don't think anyone
>> ever decided the pain was worth supporting the complexity, but right answer
>> has always been multiple IIO devices).
> Hi Jonathan,
>
> First, many thanks for your review. I agree with you, most reasonable approach is to remove some
> complexity to ease the review. Regarding injected support, basically, bellow approach is to use
> separate IIO devices for regular and injected. But, I'll remove this, at least for now, in next patch set.
>
>> You also have at least one layer of abstraction in here that serves no
>> current purpose. Please clear that out for now. It'll make the code
>> shorter and easier to follow. If/when other parts are introduced then
>> is the time to do that transistion to having the abstraction.
>
> From your suggestion, this may end-up in a single driver file in drivers/iio.
> I think I'll try to keep simple routines like start, stop, conf_scan and so on, but
> remove indirection routines from stm32-adc.h file (e.g. stm32_adc_ops).
> Is it in line with your suggestions ?
Sure, some of those will want to be in their own functions so it sounds
about right.
>
>>
>> My first thought on the double / tripple adc handling is that you'd be better
>> off handling them as 3 separate devices then doing some 'unusual' trigger
>> handling to support the weird sequencing. Guessing you thought about that?
>> If so could you lay out your reasoning for the single driver instance approach.
>> I'm not arguing against it btw, merely want to understand your reasoning!
>
> I mainly came up with a single driver instance approach because there are basically
> 3 identical ADC instances 'mapped' in a single IP with few common resources.
> I usually see mfd are more heterogeneous and declare cells for various subsystem drivers.
> But I can try to move to mfd as you're suggesting.
> I just hope this will not bring more complexity.
If anything I suspect it'll end up simpler to read (be it a tiny bit longer
in terms of lines of code).
>
>>
>> It would be tricky given one set of channels are selectable over 3 devices
>> and there are constraints to enforce (not sampling same channel on two ADCs
>> at the same time) but not impossible... Perhaps what you have here is
>> indeed simpler!
>>
>> Whilst it's been a nasty job to review, I'm guessing writing it was
>> much worse ;) Pretty good starting point though might take a little while
>> to pin down the remaining questions on how best to handle this particular
>> monster.
> My apologies... I hope you didn't had much of a headache :-) by reading me.
> More questions bellow.
>
>> Jonathan
>>> ---
>>> drivers/iio/adc/Kconfig | 2 +
>>> drivers/iio/adc/Makefile | 1 +
>>> drivers/iio/adc/stm32/Kconfig | 34 ++
>>> drivers/iio/adc/stm32/Makefile | 4 +
>>> drivers/iio/adc/stm32/stm32-adc.c | 999 ++++++++++++++++++++++++++++++++++++
>>> drivers/iio/adc/stm32/stm32-adc.h | 442 ++++++++++++++++
>>> drivers/iio/adc/stm32/stm32f4-adc.c | 574 +++++++++++++++++++++
>>> 7 files changed, 2056 insertions(+)
>>> create mode 100644 drivers/iio/adc/stm32/Kconfig
>>> create mode 100644 drivers/iio/adc/stm32/Makefile
>>> create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
>>> create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
>>> create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index 7edcf32..5c96a55 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -583,4 +583,6 @@ config XILINX_XADC
>>> The driver can also be build as a module. If so, the module will be called
>>> xilinx-xadc.
>>> +source "drivers/iio/adc/stm32/Kconfig"
>>> +
>>> endmenu
>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>> index 7a40c04..a9dbf3a 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -13,6 +13,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
>>> obj-$(CONFIG_AD7793) += ad7793.o
>>> obj-$(CONFIG_AD7887) += ad7887.o
>>> obj-$(CONFIG_AD799X) += ad799x.o
>>> +obj-$(CONFIG_ARCH_STM32) += stm32/
>>> obj-$(CONFIG_AT91_ADC) += at91_adc.o
>>> obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
>>> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
>>> diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
>>> new file mode 100644
>>> index 0000000..245d037
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32/Kconfig
>>> @@ -0,0 +1,34 @@
>>> +#
>>> +# STM32 familly ADC drivers
>>> +#
>>> +
>>> +config STM32_ADC
>>> + tristate
>>> + select REGULATOR
>>> + select REGULATOR_FIXED_VOLTAGE
>>> + select IIO_BUFFER
>>> + select IIO_TRIGGERED_BUFFER
>>> + help
>>> + Say yes here to build the driver for the STMicroelectronics
>>> + STM32 analog-to-digital converter (ADC).
>>> +
>>> + This driver can also be built as a module. If so, the module
>>> + will be called stm32-adc.
>>> +
>>> +config STM32F4_ADC
>>> + tristate "STMicroelectronics STM32F4 adc"
>>> + depends on ARCH_STM32 || COMPILE_TEST
>>> + depends on OF
>>> + select STM32_ADC
>>> + help
>>> + Say yes here to build support for STMicroelectronics stm32f4 Analog
>>> + to Digital Converter (ADC).
>>> +
>>> + This driver can also be built as a module. If so, the module
>>> + will be called stm32f4-adc.
>>> +
>>> +config STM32_ADC_DEBUG
>>> + bool "Enable debug for stm32 ADC drivers"
>>> + depends on STM32_ADC
>>> + help
>>> + Say "yes" to enable debug messages, on stm32 ADC drivers.
>>> diff --git a/drivers/iio/adc/stm32/Makefile b/drivers/iio/adc/stm32/Makefile
>>> new file mode 100644
>>> index 0000000..83e8154
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32/Makefile
>>> @@ -0,0 +1,4 @@
>>> +# Core
>>> +subdir-ccflags-$(CONFIG_STM32_ADC_DEBUG) := -DDEBUG
>>> +obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>>> +obj-$(CONFIG_STM32F4_ADC) += stm32f4-adc.o
>>> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
>>> new file mode 100644
>>> index 0000000..1e0850d
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32/stm32-adc.c
>>> @@ -0,0 +1,999 @@
> [snip]
>
>>> +
>>> +static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
>>> + const unsigned long *scan_mask)
>>> +{
>>> + struct stm32_adc *adc = iio_priv(indio_dev);
>>> + int ret;
>>> +
>>> + ret = stm32_adc_clk_sel(adc);
>>> + if (ret) {
>>> + dev_err(&indio_dev->dev, "Clock sel failed\n");
>>> + return ret;
>>> + }
>>> +
>>> + ret = stm32_adc_enable(adc);
>>> + if (ret) {
>>> + dev_err(&indio_dev->dev, "Failed to enable adc\n");
>>> + return ret;
>>> + }
>>> +
>>> + ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
>>> + if (ret) {
>>> + dev_err(&indio_dev->dev, "Failed to configure sequence\n");
>>> + goto err_dis;
>>> + }
>> It's horrible but to end up in the 'obvious' state I'd disable the adc
>> again assuming that doesn't kill the stuff that is configured.
> I'll check this and try to come up with something.
>
>>> +
>>> + return 0;
>>> +
>>> +err_dis:
>>> + stm32_adc_disable(adc);
>>> +
>>> + return ret;
>>> +}
>>> +
> [snip]
>>> +/**
>>> + * stm32_adc_single_conv() - perform a single conversion
>>> + * @indio_dev: IIO device
>>> + * @chan: IIO channel
>>> + * @result: conversion result
>>> + *
>>> + * The function performs a single conversion on a given channel, by
>>> + * by:
>>> + * - creating scan mask with only one channel
>>> + * - using SW trigger
>>> + * - then start single conv
>>> + */
>>> +static int stm32_adc_single_conv(struct iio_dev *indio_dev,
>>> + const struct iio_chan_spec *chan,
>>> + int *val)
>>> +{
>>> + struct stm32_adc *adc = iio_priv(indio_dev);
>>> + unsigned long *scan_mask;
>>> + long timeout;
>>> + u16 result;
>>> + int ret;
>>> +
>>> + scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
>>> + GFP_KERNEL);
>> This is known maximum length... I'd just avoid the complexity of allocating
>> it like this - a comment would do the job to say it is the right length.
> Do you suggest to use a predefined variable (like unsigned long scan_mask) directly ?
> And add a more basic test on 'masklength', to be sure ?
A test would make sense as would prevent any problems if this driver
is extended to support much larger devices in future.
>
>>> + if (!scan_mask)
>>> + return -ENOMEM;
>>> +
>>> + set_bit(chan->scan_index, scan_mask);
>>> +
>>> + reinit_completion(&adc->completion);
>>> +
>>> + adc->bufi = 0;
>>> + adc->num_conv = 1;
>>> + adc->buffer = &result;
>>> +
>>> + ret = stm32_adc_conf_scan(indio_dev, scan_mask);
>>> + if (ret)
>>> + goto free;
>>> +
>>> + /* No HW trigger: conversion can be launched in SW */
>>> + ret = stm32_adc_set_trig(indio_dev, NULL);
>> Put it back again afterwards? Otherwise some nasty race conditions look
>> likely to me.. (userspace sets trigger and is about to enable the buffer
>> when along comes this code and changes it underneath).
> I'll fix this.
>
>>> + if (ret) {
>>> + dev_err(&indio_dev->dev, "Can't set SW trigger\n");
>>> + goto adc_disable;
>>> + }
>>> +
>>> + stm32_adc_conv_irq_enable(adc);
>>> +
>>> + ret = stm32_adc_start_conv(adc);
>>> + if (ret) {
>>> + dev_err(&indio_dev->dev, "Failed to start single conv\n");
>>> + goto irq_disable;
>>> + }
>>> +
>>> + timeout = wait_for_completion_interruptible_timeout(
>>> + &adc->completion, STM32_ADC_TIMEOUT);
>>> + if (timeout == 0) {
>>> + dev_warn(&indio_dev->dev, "Conversion timed out!\n");
>>> + ret = -ETIMEDOUT;
>>> + } else if (timeout < 0) {
>>> + dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
>>> + ret = -EINTR;
>>> + } else {
>>> + *val = result & STM32_RESULT_MASK;
>>> + ret = IIO_VAL_INT;
>>> + }
>>> +
>>> + if (stm32_adc_stop_conv(adc))
>>> + dev_err(&indio_dev->dev, "stop failed\n");
>>> +
>>> +irq_disable:
>>> + stm32_adc_conv_irq_disable(adc);
>>> +
>>> +adc_disable:
>>> + stm32_adc_disable(adc);
>>> +
>>> +free:
>>> + kfree(scan_mask);
>>> + adc->buffer = NULL;
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>>> + struct iio_chan_spec const *chan,
>>> + int *val, int *val2, long mask)
>>> +{
>>> + struct stm32_adc *adc = iio_priv(indio_dev);
>>> + int ret = -EINVAL;
>>> +
>>> + switch (mask) {
>>> + case IIO_CHAN_INFO_RAW:
>>> + ret = iio_device_claim_direct_mode(indio_dev);
>>> + if (ret)
>>> + return ret;
>>> + if (chan->type == IIO_VOLTAGE)
>>> + ret = stm32_adc_single_conv(indio_dev, chan, val);
>>> + iio_device_release_direct_mode(indio_dev);
>>> + break;
>>> + case IIO_CHAN_INFO_SCALE:
>>> + *val = adc->common->vref_mv;
>>> + *val2 = chan->scan_type.realbits;
>>> + ret = IIO_VAL_FRACTIONAL_LOG2;
>>> + break;
>>> + default:
>>> + break;
>>> + }
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +/**
>>> + * stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
>> As this is kernel doc please document the parameter as well. Otherwise
>> we'll get a pile of warnings!
> Sure.
>>> + */
>>> +static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
>>> +{
>>> + struct iio_dev *indio_dev = iio_priv_to_dev(adc);
>>> + const struct stm32_adc_reginfo *reginfo =
>>> + adc->common->data->adc_reginfo;
>>> + u32 mask, clr_mask, status = stm32_adc_readl(adc, reginfo->isr);
>>> +
>>> + if (adc->injected) {
>>> + mask = reginfo->jeoc;
>>> + clr_mask = mask;
>>> + } else {
>>> + mask = reginfo->eoc;
>>> + /* don't clear 'eoc' as it is cleared when reading 'dr' */
>>> + clr_mask = 0;
>>> + }
>>> +
>>> + /* clear irq */
>>> + stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
>> Want to do this in the non injected case? it's a noop isn't it?
> I'll rework this.
>
>>
>>> + status &= mask;
>>> +
>>> + /* Regular data */
>>> + if (status & reginfo->eoc) {
>> Hmm.. this is a little bit of 'missuse' of the standard trigger architecture
>> but as long as it's restricted to just this device I don't suppose we need
>> to care. Only reason we need it is to provide control of 'which' hardware
>> trigger is being used.
>>
>> Guessing the DMA will almost always be turned on and will make this oddity
>> effectively disappear.
> I'm not sure to understand your remark. Above test checks end of conversion status flag.
> Or do you talk about bellow lines ? Can you please clarify ?
Took me a while to figure this out. (i.e. I wasn't sure what I meant either!)
This setup corresponds (more or less) to having an external trigger fire
off a software based sequencer. So we'd expect the 'loop' element of this
to run in the buffer handler rather than the trigger handler. In theory
that would allow other triggers to be used as well as the ones supported
in hardware.
Here that is somewhat of a pain however. If there weren't multiple triggers
to select between I'd just suggest dropping the trigger interface entirely
(it's optional) but then we'd have to do something custom to select
which of the device supplied triggers to use.
Hence probably best plan is leave it as it is. Sometimes hardware
just doesn't fit the conceptual model we have for it!
>
>>> + adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
>>> + if (iio_buffer_enabled(indio_dev)) {
>>> + adc->bufi++;
>>> + if (adc->bufi >= adc->num_conv) {
>>> + stm32_adc_conv_irq_disable(adc);
>>> + iio_trigger_poll(indio_dev->trig);
>>> + }
>>> + } else {
>>> + complete(&adc->completion);
>>> + }
>>> + }
>>> +
>>> + /* Injected data */
>>> + if (status & reginfo->jeoc) {
>>> + int i;
>>> +
>>> + for (i = 0; i < adc->num_conv; i++) {
>>> + adc->buffer[i] = stm32_adc_readl(adc, reginfo->jdr[i]);
>>> + adc->bufi++;
>>> + }
>>> +
>>> + if (iio_buffer_enabled(indio_dev)) {
>>> + stm32_adc_conv_irq_disable(adc);
>>> + iio_trigger_poll(indio_dev->trig);
>>> + } else {
>>> + complete(&adc->completion);
>>> + }
>>> + }
>>> +
>>> + /*
>>> + * In case end of conversion flags have been handled, this has been
>>> + * handled for this ADC instance
>>> + */
>>> + if (status)
>>> + return IRQ_HANDLED;
>>> +
>>> + /* This adc instance didn't trigger this interrupt */
>>> + return IRQ_NONE;
>>> +}
>>> +
>>> +/**
>>> + * stm32_adc_common_isr() - Common isr for the whole ADC block
>>> + *
>>> + * There is one IRQ for all ADCs in ADC block, check all instances.
>>> + */
>>> +static irqreturn_t stm32_adc_common_isr(int irq, void *data)
>>> +{
>>> + struct stm32_adc_common *common = data;
>>> + irqreturn_t ret = IRQ_NONE;
>>> + struct stm32_adc *adc;
>>> +
>>> + list_for_each_entry(adc, &common->adc_list, adc_list)
>>> + ret |= stm32_adc_isr(adc);
>> Hmm.. ret |= is rather fragile. Preferable to make the handling of NONE
>> vs IRQ_HANDLED explicit.
>>
>> If you were to split the driver up as I suggested might make sense above,
>> then this would be done with an irq chip in a top level device (effectively
>> a very simple mfd).
> I'll look into it.
>
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +/**
>>> + * stm32_adc_validate_trigger() - validate trigger for stm32 adc
>>> + * @indio_dev: IIO device
>>> + * @trig: new trigger
>>> + *
>>> + * Returns: 0 if trig matches one of the triggers registered by stm32 adc
>>> + * driver, -EINVAL otherwise.
>>> + */
>>> +static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
>>> + struct iio_trigger *trig)
>>> +{
>>> + return stm32_adc_get_trig_index(indio_dev, trig) < 0 ? -EINVAL : 0;
>>> +}
>>> +
>>> +static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
>>> + const unsigned long *scan_mask)
>> I'm glad you kept this relatively simple compared to some of the
>> 'fun' the hardware is capable of. Very wise!
>>> +{
>>> + struct stm32_adc *adc = iio_priv(indio_dev);
>>> + int ret;
>>> + u32 bit;
>>> +
>>> + adc->num_conv = 0;
>>> + for_each_set_bit(bit, scan_mask, indio_dev->masklength)
>>> + adc->num_conv++;
>>> +
>>> + ret = stm32_adc_conf_scan(indio_dev, scan_mask);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
>>> + const struct of_phandle_args *iiospec)
>>> +{
>>> + int i;
>>> +
>>> + for (i = 0; i < indio_dev->num_channels; i++)
>>> + if (indio_dev->channels[i].channel == iiospec->args[0])
>>> + return i;
>>> +
>>> + return -EINVAL;
>>> +}
>>> +
>>> +/**
>>> + * stm32_adc_debugfs_reg_access - read or write register value
>>> + *
>>> + * To read a value from an ADC register:
>>> + * echo [ADC reg offset] > direct_reg_access
>>> + * cat direct_reg_access
>>> + *
>>> + * To write a value in a ADC register:
>>> + * echo [ADC_reg_offset] [value] > direct_reg_access
>>> + */
>>> +static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
>>> + unsigned reg, unsigned writeval,
>>> + unsigned *readval)
>>> +{
>>> + struct stm32_adc *adc = iio_priv(indio_dev);
>>> +
>>> + if (!readval)
>>> + stm32_adc_writel(adc, reg, writeval);
>>> + else
>>> + *readval = stm32_adc_readl(adc, reg);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct iio_info stm32_adc_iio_info = {
>>> + .read_raw = stm32_adc_read_raw,
>>> + .validate_trigger = stm32_adc_validate_trigger,
>>> + .update_scan_mode = stm32_adc_update_scan_mode,
>>> + .debugfs_reg_access = stm32_adc_debugfs_reg_access,
>>> + .of_xlate = stm32_adc_of_xlate,
>>> + .driver_module = THIS_MODULE,
>>> +};
>>> +
>>> +static int stm32_adc_buffer_postdisable(struct iio_dev *indio_dev)
>>> +{
>>> + struct stm32_adc *adc = iio_priv(indio_dev);
>>> +
>>> + stm32_adc_disable(adc);
>> This is a surprise as postdisbale should balance preenable...
>> Ah, you have update scan mode enabling the adc. If you can balance it
>> better by moving that to preenable please do as it is more 'obviously' correct.
> I'll try to rework this.
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
>>> + .postenable = &iio_triggered_buffer_postenable,
>>> + .predisable = &iio_triggered_buffer_predisable,
>>> + .postdisable = &stm32_adc_buffer_postdisable,
>>> +};
>>> +
>>> +static int stm32_adc_validate_device(struct iio_trigger *trig,
>>> + struct iio_dev *indio_dev)
>>> +{
>>> + struct iio_dev *indio = iio_trigger_get_drvdata(trig);
>>> +
>>> + return indio != indio_dev ? -EINVAL : 0;
>>> +}
>>> +
>>> +static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
>>> + bool state)
>>> +{
>>> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
>>> + struct stm32_adc *adc = iio_priv(indio_dev);
>>> + int ret;
>>> +
>>> + if (state) {
>>> + /* Reset adc buffer index */
>>> + adc->bufi = 0;
>>> +
>>> + /* Allocate adc buffer */
>>> + adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
>> I'd be more cynical. It's not that big a memory allocation at worst.
>> Just put a big enough buffer in your adc structure and don't bother doing
>> it dynamically.
>>
>> If you didn't want to do it, it should be in the preenable callback rather
>> than the trigger state one (for semantic reasons rather than because it's a
>> bug)
> I'll fix this.
>>> + if (!adc->buffer)
>>> + return -ENOMEM;
>>> +
>>> + ret = stm32_adc_set_trig(indio_dev, trig);
>>> + if (ret) {
>>> + dev_err(&indio_dev->dev, "Can't set trigger\n");
>>> + goto err_buffer_free;
>>> + }
>>> +
>>> + stm32_adc_conv_irq_enable(adc);
>>> +
>>> + ret = stm32_adc_start_conv(adc);
>>> + if (ret) {
>>> + dev_err(&indio_dev->dev, "Failed to start\n");
>>> + goto err_irq_trig_disable;
>>> + }
>>> + } else {
>>> + ret = stm32_adc_stop_conv(adc);
>>> + if (ret < 0) {
>>> + dev_err(&indio_dev->dev, "Failed to stop\n");
>>> + return ret;
>>> + }
>>> +
>>> + stm32_adc_conv_irq_disable(adc);
>>> +
>>> + ret = stm32_adc_set_trig(indio_dev, NULL);
>>> + if (ret)
>>> + dev_warn(&indio_dev->dev, "Can't clear trigger\n");
>>> +
>>> + kfree(adc->buffer);
>>> + adc->buffer = NULL;
>>> + }
>>> +
>>> + return 0;
>>> +
>>> +err_irq_trig_disable:
>>> + stm32_adc_conv_irq_disable(adc);
>>> + stm32_adc_set_trig(indio_dev, NULL);
>>> +
>>> +err_buffer_free:
>>> + kfree(adc->buffer);
>>> + adc->buffer = NULL;
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static const struct iio_trigger_ops stm32_adc_trigger_ops = {
>>> + .owner = THIS_MODULE,
>>> + .validate_device = stm32_adc_validate_device,
>>> + .set_trigger_state = stm32_adc_set_trigger_state,
>>> +};
>>> +
>>> +static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
>>> +{
>>> + struct iio_poll_func *pf = p;
>>> + struct iio_dev *indio_dev = pf->indio_dev;
>>> + struct stm32_adc *adc = iio_priv(indio_dev);
>>> +
>>> + dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
>>> +
>>> + /* reset buffer index */
>>> + adc->bufi = 0;
>>> + iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
>>> + pf->timestamp);
>>> +
>>> + iio_trigger_notify_done(indio_dev->trig);
>>> +
>>> + /* re-enable eoc irq */
>>> + stm32_adc_conv_irq_enable(adc);
>>> +
>>> + return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void stm32_adc_trig_unregister(struct iio_dev *indio_dev)
>>> +{
>>> + struct stm32_adc *adc = iio_priv(indio_dev);
>>> + struct iio_trigger *trig, *_t;
>>> +
>>> + list_for_each_entry_safe(trig, _t, &adc->extrig_list, alloc_list) {
>>> + iio_trigger_unregister(trig);
>>> + list_del(&trig->alloc_list);
>>> + }
>>> +}
>>> +
>> I'd like a bit of documentation on this and a few of the other more
>> complex functions. Here it wasn't immediately obvious to me that it
>> was registering a large set of triggers. Also, silly question but
>> do you have any means of controlling the various timer setups from userspace?
> Sorry about this, I'll try to comment about trigger list to make it more obvious.
> There is no mean to setup timers via userspace, yet...
> I can remove them from the list for now, until this is supported.
> BTW I have some questions on trigger...
>
>>
>> There have been numerous discussions over the years on having a generic
>> timer subsystem, but if anything got written it passed me by. I have a couple
>> of boards where it would be handy but never had the time to do more than
>> talk about it ;)
>
> This is interesting... I'd be glad to hear more about it. Can you
> point some discussions if you have it in mind?
Err. Not sure I'll actually find an email thread on this. I can find
a reference to earlier discussions on it in the original IIO submission
over 8 years ago... So it was before we had an IIO list which means
it must have been on lkml.
As far as I know, no one ever took this futher though...
>
> In this driver, validate_trigger routine enforces that only triggers
> allocated for current indio_dev can be used. What if all timer
> triggers are put in a separate driver ? (e.g. like hrtimer in
> drivers/iio/trigger/) ? Purpose would be to tune 'sampling_frequency'
> and so on, on similar model, and have it configured basically when
> using it (e.g. cat trigger/name>trigger/current_trigger.).
I think we'd be closer to having a timer subsystem offer some
'services' to the drivers and then have your driver make use of those.
As the timer is hard wired to the actual adc timing here (rather than
a signal passing through kernel space like the high resolution timer
triggers are) I think the ADC driver will need to know about it
directly in some fashion.
>
> Is it a viable option, not to declare timer triggers in stm32-adc.c,
> but use pre-defined list of triggers, and separate trigger driver ?
> I'm thinking then, of simple string based list... But maybe you
> already though about this king of things ?
A simple string based identification might be prone to problems as
any driver could define it's own naming.
So I think the triggers need to be supplied by stm32-adc.c as the
driver needs to know about the 'hard wired' nature of which timers
can be used. The underlying handling of timer configuring etc
might be provided by a separate 'provider' module - similar to we
do for clock sources or regulators for example. Both of these
have the same characteristic of being separate 'hardware blocks'
that can be connected to lots of things, but are in this case
directly feeding the devices using them.
(we do this with IIO consumers too but that is a different game
and not relevant here + harder to follow than simple regs and
clocks).
>
> Please kindly share you view on this.
It's a non trivial job, but if you ultimately want to be able
to use those periodic timers for multiple possible purposes then
you'll need to do a fair bit of the work towards a generic
subsystem for timers anyway (callbacks etc).
Whether it is worth supporting the more 'soft' connected
equivalents (blackfin timer which is in staging as an IIO trigger
or the pxa271 periodic timer driver I wrote years ago - which
cheated and created a pile of RTCs to expose the interface)
is unclear. A lot of those usecases are well handled by
the High Resolution Timer trigger.
Jonathan
>
> Thanks again for your review.
> Best Regards,
> Fabrice
>
>>> +static int stm32_adc_trig_register(struct iio_dev *indio_dev)
>>> +{
>>> + struct stm32_adc *adc = iio_priv(indio_dev);
>>> + struct stm32_adc_common *common = adc->common;
>>> + const struct stm32_adc_trig_info *ext = common->data->ext_triggers;
>>> + struct iio_trigger *trig;
>>> + int i, ret = 0;
>>> +
>>> + if (adc->injected)
>>> + ext = common->data->jext_triggers;
>>> + else
>>> + ext = common->data->ext_triggers;
>>> +
>>> + for (i = 0; ext && ext[i].name; i++) {
>>> + trig = devm_iio_trigger_alloc(common->dev, "%s_%s%d_%s",
>>> + indio_dev->name,
>>> + adc->injected ? "jext" : "ext",
>>> + ext[i].extsel, ext[i].name);
>>> + if (!trig) {
>>> + dev_err(common->dev, "trig %s_%s%d_%s alloc failed\n",
>>> + indio_dev->name,
>>> + adc->injected ? "jext" : "ext",
>>> + ext[i].extsel, ext[i].name);
>>> + ret = -ENOMEM;
>>> + goto err;
>>> + }
>>> +
>>> + trig->dev.parent = common->dev;
>>> + trig->ops = &stm32_adc_trigger_ops;
>>> + iio_trigger_set_drvdata(trig, indio_dev);
>>> +
>>> + ret = iio_trigger_register(trig);
>>> + if (ret) {
>>> + dev_err(common->dev,
>>> + "trig %s_%s%d_%s register failed\n",
>>> + indio_dev->name,
>>> + adc->injected ? "jext" : "ext",
>>> + ext[i].extsel, ext[i].name);
>>> + goto err;
>>> + }
>>> +
>>> + list_add_tail(&trig->alloc_list, &adc->extrig_list);
>>> + }
>>> +
>>> + return 0;
>>> +err:
>>> + stm32_adc_trig_unregister(indio_dev);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>>> + struct iio_chan_spec *chan,
>>> + const struct stm32_adc_chan_spec *channel,
>>> + int scan_index)
>>> +{
>>> + struct stm32_adc *adc = iio_priv(indio_dev);
>>> +
>>> + chan->type = channel->type;
>>> + chan->channel = channel->channel;
>>> + chan->datasheet_name = channel->name;
>>> + chan->extend_name = channel->name;
>>> + chan->scan_index = scan_index;
>>> + chan->indexed = 1;
>>> + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
>>> + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
>>> + chan->scan_type.sign = 'u';
>>> + chan->scan_type.realbits = adc->common->data->highres;
>>> + chan->scan_type.storagebits = STM32_STORAGEBITS;
>> This is one of those cases where actually I'd argue just having the number
>> here and not under a define would be clearer! So just put 16 here.
>>> + chan->scan_type.shift = 0;
>> Should be unneeded. Shift of 0 is the obvious default so no info provided
>> to readers of the code either really.
> I'll fix this
>>> +}
>>> +
>>> +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
>>> + const struct stm32_adc_info *adc_info)
>>> +{
>>> + struct stm32_adc *adc = iio_priv(indio_dev);
>>> + struct device_node *node = indio_dev->dev.of_node;
>>> + struct property *prop;
>>> + const __be32 *cur;
>>> + struct iio_chan_spec *channels;
>>> + int scan_index = 0, num_channels = 0;
>>> + u32 val;
>>> +
>>> + of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
>>> + num_channels++;
>>> +
>>> + channels = devm_kcalloc(&indio_dev->dev, num_channels,
>>> + sizeof(struct iio_chan_spec), GFP_KERNEL);
>>> + if (!channels)
>>> + return -ENOMEM;
>>> +
>>> + of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
>>> + stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
>>> + &adc_info->channels[val],
>>> + scan_index);
>>> + scan_index++;
>>> + }
>>> +
>>> + adc->max_channels = adc_info->max_channels;
>>> + indio_dev->num_channels = scan_index;
>>> + indio_dev->channels = channels;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int stm32_adc_register(struct stm32_adc_common *common,
>>> + struct device_node *child)
>>> +{
>>> + struct iio_dev *indio_dev;
>>> + struct stm32_adc *adc;
>>> + int i, ret;
>>> + u32 reg;
>>> +
>>> + ret = of_property_read_u32(child, "reg", ®);
>>> + if (ret != 0) {
>>> + dev_err(common->dev, "missing reg property\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + for (i = 0; common->data->adc_info[i].channels; i++)
>>> + if (common->data->adc_info[i].reg == reg)
>>> + break;
>>> +
>>> + if (i >= STM32_ADC_ID_MAX || !common->data->adc_info[i].channels) {
>>> + dev_err(common->dev, "bad adc reg offset\n");
>>> + return -ENOENT;
>>> + }
>>> +
>>> + indio_dev = devm_iio_device_alloc(common->dev, sizeof(*adc));
>>> + if (!indio_dev) {
>>> + dev_err(common->dev, "iio device allocation failed\n");
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + adc = iio_priv(indio_dev);
>>> + adc->id = i;
>>> + adc->offset = reg;
>>> + adc->common = common;
>>> + INIT_LIST_HEAD(&adc->extrig_list);
>>> + spin_lock_init(&adc->lock);
>>> + init_completion(&adc->completion);
>>> +
>>> + if (child->name)
>>> + indio_dev->name = child->name;
>>> + else
>>> + indio_dev->name = common->data->adc_info[i].name;
>>> + indio_dev->dev.parent = common->dev;
>>> + indio_dev->dev.of_node = child;
>>> + indio_dev->info = &stm32_adc_iio_info;
>>> + indio_dev->modes = INDIO_DIRECT_MODE;
>>> +
>>> + if (of_property_read_bool(child, "st,injected")) {
>>> + dev_dbg(common->dev, "%s Configured to use injected\n",
>>> + indio_dev->name);
>>> + adc->injected = true;
>>> + }
>>> +
>>> + adc->clk = of_clk_get(child, 0);
>>> + if (IS_ERR(adc->clk)) {
>>> + adc->clk = NULL;
>>> + dev_dbg(common->dev, "No child clk found\n");
>>> + } else {
>>> + ret = clk_prepare_enable(adc->clk);
>>> + if (ret < 0)
>>> + goto err_clk_put;
>>> + }
>>> +
>>> + ret = stm32_adc_chan_of_init(indio_dev, &common->data->adc_info[i]);
>>> + if (ret < 0) {
>>> + dev_err(common->dev, "iio channels init failed\n");
>>> + goto err_clk_disable;
>>> + }
>>> +
>>> + ret = stm32_adc_trig_register(indio_dev);
>>> + if (ret)
>>> + goto err_clk_disable;
>>> +
>>> + ret = iio_triggered_buffer_setup(indio_dev,
>>> + &iio_pollfunc_store_time,
>>> + &stm32_adc_trigger_handler,
>>> + &iio_triggered_buffer_setup_ops);
>>> + if (ret) {
>>> + dev_err(common->dev, "buffer setup failed\n");
>>> + goto err_trig_unregister;
>>> + }
>>> +
>>> + ret = iio_device_register(indio_dev);
>>> + if (ret) {
>>> + dev_err(common->dev, "iio dev register failed\n");
>>> + goto err_buffer_cleanup;
>>> + }
>>> +
>>> + list_add_tail(&adc->adc_list, &common->adc_list);
>>> +
>>> + return 0;
>>> +
>>> +err_buffer_cleanup:
>>> + iio_triggered_buffer_cleanup(indio_dev);
>>> +
>>> +err_trig_unregister:
>>> + stm32_adc_trig_unregister(indio_dev);
>>> +
>>> +err_clk_disable:
>>> + if (adc->clk)
>>> + clk_disable_unprepare(adc->clk);
>>> +
>>> +err_clk_put:
>>> + if (adc->clk)
>>> + clk_put(adc->clk);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static void stm32_adc_unregister(struct stm32_adc *adc)
>>> +{
>>> + struct iio_dev *indio_dev = iio_priv_to_dev(adc);
>>> +
>>> + iio_device_unregister(indio_dev);
>>> + iio_triggered_buffer_cleanup(indio_dev);
>>> + stm32_adc_trig_unregister(indio_dev);
>>> + if (adc->clk) {
>>> + clk_disable_unprepare(adc->clk);
>>> + clk_put(adc->clk);
>>> + }
>>> +}
>>> +
>>> +int stm32_adc_probe(struct platform_device *pdev)
>>> +{
>>> + struct device_node *np = pdev->dev.of_node, *child;
>>> + struct device *dev = &pdev->dev;
>>> + const struct of_device_id *match;
>>> + struct stm32_adc_common *common;
>>> + struct stm32_adc *adc;
>>> + struct resource *res;
>>> + int ret;
>>> +
>>> + match = of_match_device(dev->driver->of_match_table, &pdev->dev);
>>> + if (!match || !match->data) {
>>> + dev_err(&pdev->dev, "compatible data not provided\n");
>> How would we have instantiated this if there was not a suitable match?
>> As such what does this check give us? (confused!)
> I'll fix this.
>>> + return -EINVAL;
>>> + }
>>> +
>>> + common = devm_kzalloc(&pdev->dev, sizeof(*common), GFP_KERNEL);
>>> + if (!common)
>>> + return -ENOMEM;
>>> +
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + common->base = devm_ioremap_resource(&pdev->dev, res);
>>> + if (IS_ERR(common->base))
>>> + return PTR_ERR(common->base);
>>> +
>>> + common->data = match->data;
>>> + common->dev = &pdev->dev;
>>> + platform_set_drvdata(pdev, common);
>>> + mutex_init(&common->lock);
>>> + INIT_LIST_HEAD(&common->adc_list);
>>> +
>>> + common->vref = devm_regulator_get(&pdev->dev, "vref");
>>> + if (IS_ERR(common->vref)) {
>>> + ret = PTR_ERR(common->vref);
>>> + dev_err(&pdev->dev, "vref get failed, %d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + ret = regulator_enable(common->vref);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "vref enable failed\n");
>>> + return ret;
>>> + }
>>> +
>>> + ret = regulator_get_voltage(common->vref);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
>>> + goto err_regulator_disable;
>>> + }
>>> + common->vref_mv = ret / 1000;
>>> + dev_dbg(&pdev->dev, "vref+=%dmV\n", common->vref_mv);
>>> +
>>> + common->aclk = devm_clk_get(&pdev->dev, "adc");
>>> + if (IS_ERR(common->aclk)) {
>>> + ret = PTR_ERR(common->aclk);
>>> + dev_err(&pdev->dev, "Can't get 'adc' clock\n");
>>> + goto err_regulator_disable;
>>> + }
>>> +
>>> + ret = clk_prepare_enable(common->aclk);
>>> + if (ret < 0) {
>>> + dev_err(common->dev, "adc clk enable failed\n");
>>> + goto err_regulator_disable;
>>> + }
>>> +
>>> + common->irq = platform_get_irq(pdev, 0);
>>> + if (common->irq < 0) {
>>> + dev_err(&pdev->dev, "failed to get irq\n");
>>> + ret = common->irq;
>>> + goto err_clk_disable;
>>> + }
>>> +
>>> + ret = devm_request_irq(&pdev->dev, common->irq, stm32_adc_common_isr,
>>> + 0, pdev->name, common);
>>> + if (ret) {
>>> + dev_err(&pdev->dev, "failed to request irq\n");
>>> + goto err_clk_disable;
>>> + }
>>> +
>>> + /* Parse adc child nodes to retrieve master/slave instances data */
>>> + for_each_available_child_of_node(np, child) {
>>> + ret = stm32_adc_register(common, child);
>>> + if (ret)
>>> + goto err_unregister;
>>> + }
>>> +
>>> + dev_info(&pdev->dev, "registered\n");
>> No benefit in this info being provided (it's obvious, device just turned up
>> in sysfs :) So drop it.
> I'll fix this.
>>> +
>>> + return 0;
>>> +
>>> +err_unregister:
>>> + list_for_each_entry(adc, &common->adc_list, adc_list)
>>> + stm32_adc_unregister(adc);
>>> +
>>> +err_clk_disable:
>>> + clk_disable_unprepare(common->aclk);
>>> +
>>> +err_regulator_disable:
>>> + regulator_disable(common->vref);
>>> +
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(stm32_adc_probe);
>>> +
>>> +int stm32_adc_remove(struct platform_device *pdev)
>>> +{
>>> + struct stm32_adc_common *common = platform_get_drvdata(pdev);
>>> + struct stm32_adc *adc;
>>> +
>>> + list_for_each_entry(adc, &common->adc_list, adc_list)
>>> + stm32_adc_unregister(adc);
>>> + clk_disable_unprepare(common->aclk);
>>> + regulator_disable(common->vref);
>>> +
>>> + return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(stm32_adc_remove);
>>> +
>>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>>> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
>>> new file mode 100644
>>> index 0000000..0be603c
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32/stm32-adc.h
>>> @@ -0,0 +1,442 @@
>>> +/*
>>> + * This file is part of STM32 ADC driver
>>> + *
>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>>> + *
>>> + * License type: GPLv2
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>> + * See the GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#ifndef __STM32_ADC_H
>>> +#define __STM32_ADC_H
>>> +
>>> +/*
>>> + * STM32 - ADC global register map
>>> + * ________________________________________________________
>>> + * | Offset | Register |
>>> + * --------------------------------------------------------
>>> + * | 0x000 | Master ADC1 |
>>> + * --------------------------------------------------------
>>> + * | 0x100 | Slave ADC2 |
>>> + * --------------------------------------------------------
>>> + * | 0x200 | Slave ADC3 |
>>> + * --------------------------------------------------------
>>> + * | 0x300 | Master & Slave common regs |
>>> + * --------------------------------------------------------
>>> + */
>>> +#define STM32_ADCX_COMN_OFFSET 0x300
>>> +#define STM32_ADC_ID_MAX 3
>>> +#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */
>>> +#define STM32_ADC_MAX_JSQ 4 /* JSQ1..JSQ4 */
>>> +
>>> +/* STM32 value masks */
>>> +#define STM32_RESULT_MASK GENMASK(15, 0)
>>> +#define STM32_STORAGEBITS 16
>>> +
>>> +/* External trigger enable for regular or injected channels (exten/jexten) */
>>> +enum stm32_adc_exten {
>>> + STM32_EXTEN_SWTRIG,
>>> + STM32_EXTEN_HWTRIG_RISING_EDGE,
>>> + STM32_EXTEN_HWTRIG_FALLING_EDGE,
>>> + STM32_EXTEN_HWTRIG_BOTH_EDGES,
>>> +};
>>> +
>>> +enum stm32_adc_extsel {
>>> + STM32_EXT0,
>>> + STM32_EXT1,
>>> + STM32_EXT2,
>>> + STM32_EXT3,
>>> + STM32_EXT4,
>>> + STM32_EXT5,
>>> + STM32_EXT6,
>>> + STM32_EXT7,
>>> + STM32_EXT8,
>>> + STM32_EXT9,
>>> + STM32_EXT10,
>>> + STM32_EXT11,
>>> + STM32_EXT12,
>>> + STM32_EXT13,
>>> + STM32_EXT14,
>>> + STM32_EXT15,
>>> + STM32_EXT16,
>>> + STM32_EXT17,
>>> + STM32_EXT18,
>>> + STM32_EXT19,
>>> + STM32_EXT20,
>>> + STM32_EXT21,
>>> + STM32_EXT22,
>>> + STM32_EXT23,
>>> + STM32_EXT24,
>>> + STM32_EXT25,
>>> + STM32_EXT26,
>>> + STM32_EXT27,
>>> + STM32_EXT28,
>>> + STM32_EXT29,
>>> + STM32_EXT30,
>>> + STM32_EXT31,
>>> +};
>>> +
>>> +enum stm32_adc_jextsel {
>>> + STM32_JEXT0,
>>> + STM32_JEXT1,
>>> + STM32_JEXT2,
>>> + STM32_JEXT3,
>>> + STM32_JEXT4,
>>> + STM32_JEXT5,
>>> + STM32_JEXT6,
>>> + STM32_JEXT7,
>>> + STM32_JEXT8,
>>> + STM32_JEXT9,
>>> + STM32_JEXT10,
>>> + STM32_JEXT11,
>>> + STM32_JEXT12,
>>> + STM32_JEXT13,
>>> + STM32_JEXT14,
>>> + STM32_JEXT15,
>>> + STM32_JEXT16,
>>> + STM32_JEXT17,
>>> + STM32_JEXT18,
>>> + STM32_JEXT19,
>>> + STM32_JEXT20,
>>> + STM32_JEXT21,
>>> + STM32_JEXT22,
>>> + STM32_JEXT23,
>>> + STM32_JEXT24,
>>> + STM32_JEXT25,
>>> + STM32_JEXT26,
>>> + STM32_JEXT27,
>>> + STM32_JEXT28,
>>> + STM32_JEXT29,
>>> + STM32_JEXT30,
>>> + STM32_JEXT31,
>>> +};
>>> +
>>> +#define STM32_ADC_TIMEOUT_US 100000
>>> +#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
>>> +
>>> +/**
>>> + * struct stm32_adc_chan_spec - specification of stm32 adc channel
>>> + * @type: IIO channel type
>>> + * @channel: channel number (single ended)
>>> + * @name: channel name (single ended)
>>> + */
>>> +struct stm32_adc_chan_spec {
>>> + enum iio_chan_type type;
>>> + int channel;
>>> + const char *name;
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_adc_trig_info - ADC trigger info
>>> + * @extsel: trigger selection for regular or injected
>>> + * @name: name of the trigger, corresponding to its source
>>> + */
>>> +struct stm32_adc_trig_info {
>>> + u32 extsel;
>>> + const char *name;
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_adc_info - stm32 ADC, per instance config data
>>> + * @name: default name for this instance (like "adc1")
>>> + * @reg: reg offset for this instance (e.g. 0x0 for adc1...)
>>> + * @channels: Reference to stm32 channels spec
>>> + * @max_channels: Number of single ended channels
>>> + */
>>> +struct stm32_adc_info {
>>> + const char *name;
>>> + u32 reg;
>>> + const struct stm32_adc_chan_spec *channels;
>>> + int max_channels;
>>> +};
>>> +
>>> +/**
>>> + * stm32_adc_regs - stm32 ADC misc registers & bitfield desc
>>> + * @reg: register offset
>>> + * @mask: bitfield mask
>>> + * @shift: left shift
>>> + */
>>> +struct stm32_adc_regs {
>>> + int reg;
>>> + int mask;
>>> + int shift;
>>> +};
>>> +
>>> +/**
>>> + * stm32_adc_trig_reginfo - stm32 ADC trigger control registers description
>>> + * @reg: trigger control register offset (exten/jexten)
>>> + * @exten_mask: external trigger en/polarity mask in @reg
>>> + * @exten_shift: external trigger en/polarity shift in @reg
>>> + * @extsel_mask: external trigger source mask in @reg
>>> + * @extsel_shift: external trigger source shift in @reg
>>> + */
>>> +struct stm32_adc_trig_reginfo {
>>> + u32 reg;
>>> + u32 exten_mask;
>>> + u32 exten_shift;
>>> + u32 extsel_mask;
>>> + u32 extsel_shift;
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_adc_reginfo - stm32 ADC registers description
>>> + * @isr: interrupt status register offset
>>> + * @eoc: end of conversion mask in @isr
>>> + * @jeoc: end of injected conversion sequence mask in @isr
>>> + * @ier: interrupt enable register offset
>>> + * @eocie: end of conversion interrupt enable mask in @ier
>>> + * @jeocie: end of injected conversion sequence interrupt en mask
>>> + * @dr: data register offset
>>> + * @jdr: injected data registers offsets
>>> + * @sqr_regs: Regular sequence registers description
>>> + * @jsqr_reg: Injected sequence register description
>>> + * @trig_reginfo: regular trigger control registers description
>>> + * @jtrig_reginfo: injected trigger control registers description
>>> + */
>>> +struct stm32_adc_reginfo {
>>> + u32 isr;
>>> + u32 eoc;
>>> + u32 jeoc;
>>> + u32 ier;
>>> + u32 eocie;
>>> + u32 jeocie;
>>> + u32 dr;
>>> + u32 jdr[4];
>>> + const struct stm32_adc_regs *sqr_regs;
>>> + const struct stm32_adc_regs *jsqr_reg;
>>> + const struct stm32_adc_trig_reginfo *trig_reginfo;
>>> + const struct stm32_adc_trig_reginfo *jtrig_reginfo;
>>> +};
>>> +
>>> +struct stm32_adc;
>>> +
>>> +/**
>>> + * struct stm32_adc_ops - stm32 ADC, compatible dependent data
>>> + * - stm32 ADC may work as single ADC, or as tightly coupled master/slave ADCs.
>>> + *
>>> + * @adc_info: Array spec for stm32 adc master/slaves instances
>>> + * @ext_triggers: Reference to trigger info for regular channels
>>> + * @jext_triggers: Reference to trigger info for injected channels
>>> + * @adc_reginfo: stm32 ADC registers description
>>> + * @highres: Max resolution
>>> + * @max_clock_rate: Max input clock rate
>>> + * @clk_sel: routine to select common clock and prescaler
>>> + * @start_conv: routine to start conversions
>>> + * @stop_conv: routine to stop conversions
>>> + * @is_started: routine to get adc 'started' state
>>> + * @regular_started routine to check regular conversions status
>>> + * @injected_started routine to check injected conversions status
>>> + * @enable: optional routine to enable stm32 adc
>>> + * @disable: optional routine to disable stm32 adc
>>> + * @is_enabled reports enabled state
>>> + */
>> This is a big chunk of abstraction that seems excessive at the moment.
>> I'd rather see it introduced only just before it's actually used..
>> (I'm guessing it's intended for support of similar parts?)
>>
>> Right now it just makes the driver harder to review.
>>> +struct stm32_adc_ops {
>>> + const struct stm32_adc_info *adc_info;
>>> + const struct stm32_adc_trig_info *ext_triggers;
>>> + const struct stm32_adc_trig_info *jext_triggers;
>>> + const struct stm32_adc_reginfo *adc_reginfo;
>>> + int highres;
>>> + unsigned long max_clock_rate;
>>> + int (*clk_sel)(struct stm32_adc *adc);
>>> + int (*start_conv)(struct stm32_adc *adc);
>>> + int (*stop_conv)(struct stm32_adc *adc);
>>> + bool (*is_started)(struct stm32_adc *adc);
>>> + bool (*regular_started)(struct stm32_adc *adc);
>>> + bool (*injected_started)(struct stm32_adc *adc);
>>> + int (*enable)(struct stm32_adc *adc);
>>> + void (*disable)(struct stm32_adc *adc);
>>> + bool (*is_enabled)(struct stm32_adc *adc);
>>> +};
>>> +
>>> +struct stm32_adc_common;
>>> +
>>> +/**
>>> + * struct stm32_adc - private data of each ADC IIO instance
>>> + * @common: reference to ADC block common data
>>> + * @adc_list: current ADC entry in common ADC list
>>> + * @id: ADC instance number (e.g. adc 1, 2 or 3)
>>> + * @offset: ADC instance register offset in ADC block
>>> + * @max_channels: Max channels number for this ADC.
>>> + * @extrig_list: External trigger list (for regular channel)
>>> + * @completion: end of single conversion completion
>>> + * @buffer: data buffer
>>> + * @bufi: data buffer index
>>> + * @num_conv: expected number of scan conversions
>>> + * @injected: use injected channels on this adc
>>> + * @lock: spinlock
>>> + * @clk: optional adc clock, for this adc instance
>>> + * @calib: optional calibration data
>>> + * @en: emulates enabled state on some stm32 adc
>>> + */
>>> +struct stm32_adc {
>>> + struct stm32_adc_common *common;
>>> + struct list_head adc_list;
>>> + int id;
>>> + int offset;
>>> + int max_channels;
>>> + struct list_head extrig_list;
>>> + struct completion completion;
>>> + u16 *buffer;
>>> + int bufi;
>>> + int num_conv;
>>> + bool injected;
>>> + spinlock_t lock; /* interrupt lock */
>>> + struct clk *clk;
>>> + void *calib;
>>> + bool en;
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_adc_common - private data of ADC driver, common to all
>>> + * ADC instances (ADC block)
>>> + * @dev: device for this controller
>>> + * @base: control registers base cpu addr
>>> + * @irq: Common irq line for all adc instances
>>> + * @data: STM32 dependent data from compatible
>>> + * @adc_list: list of all stm32 ADC in this ADC block
>>> + * @aclk: common clock for the analog circuitry
>>> + * @vref: regulator reference
>>> + * @vref_mv: vref voltage (mv)
>>> + * @lock: mutex
>>> + */
>>> +struct stm32_adc_common {
>>> + struct device *dev;
>>> + void __iomem *base;
>>> + int irq;
>>> + const struct stm32_adc_ops *data;
>>> + struct list_head adc_list;
>>> + struct clk *aclk;
>>> + struct regulator *vref;
>>> + int vref_mv;
>>> + struct mutex lock; /* read_raw lock */
>>> +};
>>> +
>>> +/* Helper routines */
>>> +static inline int stm32_adc_start_conv(struct stm32_adc *adc)
>>> +{
>>> + return adc->common->data->start_conv(adc);
>>> +}
>>> +
>>> +static inline int stm32_adc_stop_conv(struct stm32_adc *adc)
>>> +{
>>> + return adc->common->data->stop_conv(adc);
>>> +}
>>> +
>>> +static inline bool stm32_adc_is_started(struct stm32_adc *adc)
>>> +{
>>> + return adc->common->data->is_started(adc);
>>> +}
>>> +
>>> +static inline bool stm32_adc_regular_started(struct stm32_adc *adc)
>>> +{
>>> + return adc->common->data->regular_started(adc);
>>> +}
>>> +
>>> +static inline bool stm32_adc_injected_started(struct stm32_adc *adc)
>>> +{
>>> + return adc->common->data->injected_started(adc);
>>> +}
>>> +
>>> +static inline bool stm32_adc_clk_sel(struct stm32_adc *adc)
>>> +{
>>> + return adc->common->data->clk_sel(adc);
>>> +}
>>> +
>>> +static inline int stm32_adc_enable(struct stm32_adc *adc)
>>> +{
>>> + if (adc->common->data->enable)
>>> + return adc->common->data->enable(adc);
>>> +
>>> + adc->en = true;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static inline bool stm32_adc_is_enabled(struct stm32_adc *adc)
>>> +{
>>> + if (adc->common->data->is_enabled)
>>> + return adc->common->data->is_enabled(adc);
>>> + else
>>> + return adc->en;
>>> +}
>>> +
>>> +static inline void stm32_adc_disable(struct stm32_adc *adc)
>>> +{
>>> + /* Check there is no regular or injected on-going conversions */
>>> + if (stm32_adc_is_started(adc))
>>> + return;
>>> +
>>> + if (adc->common->data->disable)
>>> + adc->common->data->disable(adc);
>>> + else
>>> + adc->en = false;
>>> +}
>>> +
>>> +/* STM32 ADC registers access routines */
>>> +static inline u32 stm32_adc_common_readl(struct stm32_adc_common *com, u32 reg)
>>> +{
>>> + u32 val = readl_relaxed(com->base + reg);
>>> +
>>> + return val;
>>> +}
>>> +
>>> +static inline void stm32_adc_common_writel(struct stm32_adc_common *com,
>>> + u32 reg, u32 val)
>>> +{
>>> + writel_relaxed(val, com->base + reg);
>>> +}
>>> +
>>> +static inline u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
>>> +{
>>> + u32 val = readl_relaxed(adc->common->base + adc->offset + reg);
>>> +
>>> + return val;
>>> +}
>>> +
>>> +#define stm32_adc_readl_addr(addr) stm32_adc_readl(adc, addr)
>>> +
>>> +#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \
>>> + readx_poll_timeout(stm32_adc_readl_addr, reg, val, \
>>> + cond, sleep_us, timeout_us)
>>> +
>>> +static inline void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
>>> +{
>>> + writel_relaxed(val, adc->common->base + adc->offset + reg);
>>> +}
>>> +
>>> +static inline void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>>> +{
>>> + unsigned long flags;
>>> +
>>> + spin_lock_irqsave(&adc->lock, flags);
>>> + stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
>>> + spin_unlock_irqrestore(&adc->lock, flags);
>>> +}
>>> +
>>> +static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>>> +{
>>> + unsigned long flags;
>>> +
>>> + spin_lock_irqsave(&adc->lock, flags);
>>> + stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
>>> + spin_unlock_irqrestore(&adc->lock, flags);
>>> +}
>>> +
>>> +/* STM32 common extended attributes */
>>> +extern const struct iio_enum stm32_adc_trig_pol;
>>> +int stm32_adc_probe(struct platform_device *pdev);
>>> +int stm32_adc_remove(struct platform_device *pdev);
>>> +
>>> +#endif
>>> diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
>>> new file mode 100644
>>> index 0000000..147fe9c
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32/stm32f4-adc.c
>>> @@ -0,0 +1,574 @@
>>> +/*
>>> + * This file is part of STM32F4 ADC driver
>>> + *
>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>>> + *
>>> + * License type: GPLv2
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>> + * See the GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/trigger.h>
>>> +#include <linux/platform_device.h>
>>> +#include "stm32-adc.h"
>>> +
>>> +/*
>>> + * STM32F4 - ADC global register map
>>> + * ________________________________________________________
>>> + * | Offset | Register |
>>> + * --------------------------------------------------------
>>> + * | 0x000 | Master ADC1 |
>>> + * --------------------------------------------------------
>>> + * | 0x100 | Slave ADC2 |
>>> + * --------------------------------------------------------
>>> + * | 0x200 | Slave ADC3 |
>>> + * --------------------------------------------------------
>>> + * | 0x300 | Master & Slave common regs |
>>> + * --------------------------------------------------------
>>> + */
>>> +
>>> +/* STM32F4 - Registers for each ADC instance */
>>> +#define STM32F4_ADCX_SR 0x00
>>> +#define STM32F4_ADCX_CR1 0x04
>>> +#define STM32F4_ADCX_CR2 0x08
>>> +#define STM32F4_ADCX_SMPR1 0x0C
>>> +#define STM32F4_ADCX_SMPR2 0x10
>>> +#define STM32F4_ADCX_HTR 0x24
>>> +#define STM32F4_ADCX_LTR 0x28
>>> +#define STM32F4_ADCX_SQR1 0x2C
>>> +#define STM32F4_ADCX_SQR2 0x30
>>> +#define STM32F4_ADCX_SQR3 0x34
>>> +#define STM32F4_ADCX_JSQR 0x38
>>> +#define STM32F4_ADCX_JDR1 0x3C
>>> +#define STM32F4_ADCX_JDR2 0x40
>>> +#define STM32F4_ADCX_JDR3 0x44
>>> +#define STM32F4_ADCX_JDR4 0x48
>>> +#define STM32F4_ADCX_DR 0x4C
>>> +
>>> +/* STM32 - Master & slave registers (common for all instances: 1, 2 & 3) */
>>> +#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
>>> +#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
>>> +#define STM32F4_ADC_CDR (STM32_ADCX_COMN_OFFSET + 0x08)
>>> +
>>> +/* STM32F4_ADCX_SR - bit fields */
>>> +#define STM32F4_OVR BIT(5)
>>> +#define STM32F4_STRT BIT(4)
>>> +#define STM32F4_JSTRT BIT(3)
>>> +#define STM32F4_JEOC BIT(2)
>>> +#define STM32F4_EOC BIT(1)
>>> +#define STM32F4_AWD BIT(0)
>>> +
>>> +/* STM32F4_ADCX_CR1 - bit fields */
>>> +#define STM32F4_OVRIE BIT(26)
>>> +#define STM32F4_RES_SHIFT 24
>>> +#define STM32F4_RES_MASK GENMASK(25, 24)
>>> +#define STM32F4_AWDEN BIT(23)
>>> +#define STM32F4_JAWDEN BIT(22)
>>> +#define STM32F4_DISCNUM_SHIFT 13
>>> +#define STM32F4_DISCNUM_MASK GENMASK(15, 13)
>>> +#define STM32F4_JDISCEN BIT(12)
>>> +#define STM32F4_DISCEN BIT(11)
>>> +#define STM32F4_JAUTO BIT(10)
>>> +#define STM32F4_AWDSGL BIT(9)
>>> +#define STM32F4_SCAN BIT(8)
>>> +#define STM32F4_JEOCIE BIT(7)
>>> +#define STM32F4_AWDIE BIT(6)
>>> +#define STM32F4_EOCIE BIT(5)
>>> +#define STM32F4_AWDCH_SHIFT 0
>>> +#define STM32F4_AWDCH_MASK GENMASK(4, 0)
>>> +
>>> +/* STM32F4_ADCX_CR2 - bit fields */
>>> +#define STM32F4_SWSTART BIT(30)
>>> +#define STM32F4_EXTEN_SHIFT 28
>>> +#define STM32F4_EXTEN_MASK GENMASK(29, 28)
>>> +#define STM32F4_EXTSEL_SHIFT 24
>>> +#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
>>> +#define STM32F4_JSWSTART BIT(22)
>>> +#define STM32F4_JEXTEN_SHIFT 20
>>> +#define STM32F4_JEXTEN_MASK GENMASK(21, 20)
>>> +#define STM32F4_JEXTSEL_SHIFT 16
>>> +#define STM32F4_JEXTSEL_MASK GENMASK(19, 16)
>>> +#define STM32F4_ALIGN BIT(11)
>>> +#define STM32F4_EOCS BIT(10)
>>> +#define STM32F4_DDS BIT(9)
>>> +#define STM32F4_DMA BIT(8)
>>> +#define STM32F4_CONT BIT(1)
>>> +#define STM32F4_ADON BIT(0)
>>> +
>>> +/* STM32F4_ADCX_SMPR1 - bit fields */
>>> +#define STM32F4_SMP18_SHIFT 24
>>> +#define STM32F4_SMP18_MASK GENMASK(26, 24)
>>> +#define STM32F4_SMP17_SHIFT 21
>>> +#define STM32F4_SMP17_MASK GENMASK(23, 21)
>>> +#define STM32F4_SMP16_SHIFT 18
>>> +#define STM32F4_SMP16_MASK GENMASK(20, 18)
>>> +#define STM32F4_SMP15_SHIFT 15
>>> +#define STM32F4_SMP15_MASK GENMASK(17, 15)
>>> +#define STM32F4_SMP14_SHIFT 12
>>> +#define STM32F4_SMP14_MASK GENMASK(14, 12)
>>> +#define STM32F4_SMP13_SHIFT 9
>>> +#define STM32F4_SMP13_MASK GENMASK(11, 9)
>>> +#define STM32F4_SMP12_SHIFT 6
>>> +#define STM32F4_SMP12_MASK GENMASK(8, 6)
>>> +#define STM32F4_SMP11_SHIFT 3
>>> +#define STM32F4_SMP11_MASK GENMASK(5, 3)
>>> +#define STM32F4_SMP10_SHIFT 0
>>> +#define STM32F4_SMP10_MASK GENMASK(2, 0)
>>> +
>>> +/* STM32F4_ADCX_SMPR2 - bit fields */
>>> +#define STM32F4_SMP9_SHIFT 27
>>> +#define STM32F4_SMP9_MASK GENMASK(29, 27)
>>> +#define STM32F4_SMP8_SHIFT 24
>>> +#define STM32F4_SMP8_MASK GENMASK(26, 24)
>>> +#define STM32F4_SMP7_SHIFT 21
>>> +#define STM32F4_SMP7_MASK GENMASK(23, 21)
>>> +#define STM32F4_SMP6_SHIFT 18
>>> +#define STM32F4_SMP6_MASK GENMASK(20, 18)
>>> +#define STM32F4_SMP5_SHIFT 15
>>> +#define STM32F4_SMP5_MASK GENMASK(17, 15)
>>> +#define STM32F4_SMP4_SHIFT 12
>>> +#define STM32F4_SMP4_MASK GENMASK(14, 12)
>>> +#define STM32F4_SMP3_SHIFT 9
>>> +#define STM32F4_SMP3_MASK GENMASK(11, 9)
>>> +#define STM32F4_SMP2_SHIFT 6
>>> +#define STM32F4_SMP2_MASK GENMASK(8, 6)
>>> +#define STM32F4_SMP1_SHIFT 3
>>> +#define STM32F4_SMP1_MASK GENMASK(5, 3)
>>> +#define STM32F4_SMP0_SHIFT 0
>>> +#define STM32F4_SMP0_MASK GENMASK(2, 0)
>>> +enum stm32f4_adc_smpr {
>>> + STM32F4_SMPR_3_CK_CYCLES,
>>> + STM32F4_SMPR_15_CK_CYCLES,
>>> + STM32F4_SMPR_28_CK_CYCLES,
>>> + STM32F4_SMPR_56_CK_CYCLES,
>>> + STM32F4_SMPR_84_CK_CYCLES,
>>> + STM32F4_SMPR_112_CK_CYCLES,
>>> + STM32F4_SMPR_144_CK_CYCLES,
>>> + STM32F4_SMPR_480_CK_CYCLES,
>>> +};
>>> +
>>> +/* STM32F4_ADCX_SQR1 - bit fields */
>>> +#define STM32F4_L_SHIFT 20
>>> +#define STM32F4_L_MASK GENMASK(23, 20)
>>> +#define STM32F4_SQ16_SHIFT 15
>>> +#define STM32F4_SQ16_MASK GENMASK(19, 15)
>>> +#define STM32F4_SQ15_SHIFT 10
>>> +#define STM32F4_SQ15_MASK GENMASK(14, 10)
>>> +#define STM32F4_SQ14_SHIFT 5
>>> +#define STM32F4_SQ14_MASK GENMASK(9, 5)
>>> +#define STM32F4_SQ13_SHIFT 0
>>> +#define STM32F4_SQ13_MASK GENMASK(4, 0)
>>> +
>>> +/* STM32F4_ADCX_SQR2 - bit fields */
>>> +#define STM32F4_SQ12_SHIFT 25
>>> +#define STM32F4_SQ12_MASK GENMASK(29, 25)
>>> +#define STM32F4_SQ11_SHIFT 20
>>> +#define STM32F4_SQ11_MASK GENMASK(24, 20)
>>> +#define STM32F4_SQ10_SHIFT 15
>>> +#define STM32F4_SQ10_MASK GENMASK(19, 15)
>>> +#define STM32F4_SQ9_SHIFT 10
>>> +#define STM32F4_SQ9_MASK GENMASK(14, 10)
>>> +#define STM32F4_SQ8_SHIFT 5
>>> +#define STM32F4_SQ8_MASK GENMASK(9, 5)
>>> +#define STM32F4_SQ7_SHIFT 0
>>> +#define STM32F4_SQ7_MASK GENMASK(4, 0)
>>> +
>>> +/* STM32F4_ADCX_SQR3 - bit fields */
>>> +#define STM32F4_SQ6_SHIFT 25
>>> +#define STM32F4_SQ6_MASK GENMASK(29, 25)
>>> +#define STM32F4_SQ5_SHIFT 20
>>> +#define STM32F4_SQ5_MASK GENMASK(24, 20)
>>> +#define STM32F4_SQ4_SHIFT 15
>>> +#define STM32F4_SQ4_MASK GENMASK(19, 15)
>>> +#define STM32F4_SQ3_SHIFT 10
>>> +#define STM32F4_SQ3_MASK GENMASK(14, 10)
>>> +#define STM32F4_SQ2_SHIFT 5
>>> +#define STM32F4_SQ2_MASK GENMASK(9, 5)
>>> +#define STM32F4_SQ1_SHIFT 0
>>> +#define STM32F4_SQ1_MASK GENMASK(4, 0)
>>> +
>>> +/* STM32F4_ADCX_JSQR - bit fields */
>>> +#define STM32F4_JL_SHIFT 20
>>> +#define STM32F4_JL_MASK GENMASK(21, 20)
>>> +#define STM32F4_JSQ4_SHIFT 15
>>> +#define STM32F4_JSQ4_MASK GENMASK(19, 15)
>>> +#define STM32F4_JSQ3_SHIFT 10
>>> +#define STM32F4_JSQ3_MASK GENMASK(14, 10)
>>> +#define STM32F4_JSQ2_SHIFT 5
>>> +#define STM32F4_JSQ2_MASK GENMASK(9, 5)
>>> +#define STM32F4_JSQ1_SHIFT 0
>>> +#define STM32F4_JSQ1_MASK GENMASK(4, 0)
>>> +
>>> +/* STM32F4_ADC_CCR - bit fields */
>>> +#define STM32F4_ADC_ADCPRE_SHIFT 16
>>> +#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
>>> +
>>> +/*
>>> + * stm32 ADC1, ADC2 & ADC3 are tightly coupled and may be used in multi mode
>>> + * Define here all inputs for all ADC instances
>>> + */
>>> +static const struct stm32_adc_chan_spec stm32f4_adc1_channels[] = {
>>> + /* master ADC1 */
>>> + { IIO_VOLTAGE, 0, "in0" },
>>> + { IIO_VOLTAGE, 1, "in1" },
>>> + { IIO_VOLTAGE, 2, "in2" },
>>> + { IIO_VOLTAGE, 3, "in3" },
>>> + { IIO_VOLTAGE, 4, "in4" },
>>> + { IIO_VOLTAGE, 5, "in5" },
>>> + { IIO_VOLTAGE, 6, "in6" },
>>> + { IIO_VOLTAGE, 7, "in7" },
>>> + { IIO_VOLTAGE, 8, "in8" },
>>> + { IIO_VOLTAGE, 9, "in9" },
>>> + { IIO_VOLTAGE, 10, "in10" },
>>> + { IIO_VOLTAGE, 11, "in11" },
>>> + { IIO_VOLTAGE, 12, "in12" },
>>> + { IIO_VOLTAGE, 13, "in13" },
>>> + { IIO_VOLTAGE, 14, "in14" },
>>> + { IIO_VOLTAGE, 15, "in15" },
>>> + /* internal analog sources available on input 16 to 18 */
>>> + { IIO_VOLTAGE, 16, "in16" },
>>> + { IIO_VOLTAGE, 17, "in17" },
>>> + { IIO_VOLTAGE, 18, "in18" },
>>> +};
>>> +
>>> +static const struct stm32_adc_chan_spec stm32f4_adc23_channels[] = {
>>> + /* slave ADC2 / ADC3 */
>>> + { IIO_VOLTAGE, 0, "in0" },
>>> + { IIO_VOLTAGE, 1, "in1" },
>>> + { IIO_VOLTAGE, 2, "in2" },
>>> + { IIO_VOLTAGE, 3, "in3" },
>>> + { IIO_VOLTAGE, 4, "in4" },
>>> + { IIO_VOLTAGE, 5, "in5" },
>>> + { IIO_VOLTAGE, 6, "in6" },
>>> + { IIO_VOLTAGE, 7, "in7" },
>>> + { IIO_VOLTAGE, 8, "in8" },
>>> + { IIO_VOLTAGE, 9, "in9" },
>>> + { IIO_VOLTAGE, 10, "in10" },
>>> + { IIO_VOLTAGE, 11, "in11" },
>>> + { IIO_VOLTAGE, 12, "in12" },
>>> + { IIO_VOLTAGE, 13, "in13" },
>>> + { IIO_VOLTAGE, 14, "in14" },
>>> + { IIO_VOLTAGE, 15, "in15" },
>>> +};
>>> +
>>> +/* Triggers for regular channels */
>>> +static const struct stm32_adc_trig_info stm32f4_adc_ext_triggers[] = {
>>> + { STM32_EXT0, "TIM1_CH1" },
>>> + { STM32_EXT1, "TIM1_CH2" },
>>> + { STM32_EXT2, "TIM1_CH3" },
>>> + { STM32_EXT3, "TIM2_CH2" },
>>> + { STM32_EXT4, "TIM2_CH3" },
>>> + { STM32_EXT5, "TIM2_CH4" },
>>> + { STM32_EXT6, "TIM2_TRGO" },
>>> + { STM32_EXT7, "TIM3_CH1" },
>>> + { STM32_EXT8, "TIM3_TRGO" },
>>> + { STM32_EXT9, "TIM4_CH4" },
>>> + { STM32_EXT10, "TIM5_CH1" },
>>> + { STM32_EXT11, "TIM5_CH2" },
>>> + { STM32_EXT12, "TIM5_CH3" },
>>> + { STM32_EXT13, "TIM8_CH1" },
>>> + { STM32_EXT14, "TIM8_TRGO" },
>>> + { STM32_EXT15, "EXTI_11" },
>>> + {},
>>> +};
>>> +
>>> +/* Triggers for injected channels */
>>> +static const struct stm32_adc_trig_info stm32f4_adc_jext_triggers[] = {
>>> + { STM32_JEXT0, "TIM1_CH4" },
>>> + { STM32_JEXT1, "TIM1_TRGO" },
>>> + { STM32_JEXT2, "TIM2_CH1" },
>>> + { STM32_JEXT3, "TIM2_TRGO" },
>>> + { STM32_JEXT4, "TIM3_CH2" },
>>> + { STM32_JEXT5, "TIM3_CH4" },
>>> + { STM32_JEXT6, "TIM4_CH1" },
>>> + { STM32_JEXT7, "TIM4_CH2" },
>>> + { STM32_JEXT8, "TIM4_CH3" },
>>> + { STM32_JEXT9, "TIM4_TRGO" },
>>> + { STM32_JEXT10, "TIM5_CH4" },
>>> + { STM32_JEXT11, "TIM5_TRGO" },
>>> + { STM32_JEXT12, "TIM8_CH2" },
>>> + { STM32_JEXT13, "TIM8_CH3" },
>>> + { STM32_JEXT14, "TIM8_CH4" },
>>> + { STM32_JEXT15, "EXTI_15" },
>>> + {},
>>> +};
>>> +
>>> +static const struct stm32_adc_info stm32f4_adc_info[] = {
>>> + {
>>> + .name = "adc1-master",
>>> + .reg = 0x0,
>>> + .channels = stm32f4_adc1_channels,
>>> + .max_channels = ARRAY_SIZE(stm32f4_adc1_channels),
>>> + },
>>> + {
>>> + .name = "adc2-slave",
>>> + .reg = 0x100,
>>> + .channels = stm32f4_adc23_channels,
>>> + .max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
>>> + },
>>> + {
>>> + .name = "adc3-slave",
>>> + .reg = 0x200,
>>> + .channels = stm32f4_adc23_channels,
>>> + .max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
>>> + },
>>> + {},
>>> +};
>>> +
>>> +/**
>>> + * stm32f4_sqr_regs - describe regular sequence registers
>>> + * - L: sequence len (register & bit field)
>>> + * - SQ1..SQ16: sequence entries (register & bit field)
>>> + */
>>> +static const struct stm32_adc_regs stm32f4_sqr_regs[STM32_ADC_MAX_SQ + 1] = {
>>> + /* L: len bit field description to be kept as first element */
>>> + { STM32F4_ADCX_SQR1, STM32F4_L_MASK, STM32F4_L_SHIFT },
>>> + /* SQ1..SQ16 registers & bit fields */
>>> + { STM32F4_ADCX_SQR3, STM32F4_SQ1_MASK, STM32F4_SQ1_SHIFT },
>>> + { STM32F4_ADCX_SQR3, STM32F4_SQ2_MASK, STM32F4_SQ2_SHIFT },
>>> + { STM32F4_ADCX_SQR3, STM32F4_SQ3_MASK, STM32F4_SQ3_SHIFT },
>>> + { STM32F4_ADCX_SQR3, STM32F4_SQ4_MASK, STM32F4_SQ4_SHIFT },
>>> + { STM32F4_ADCX_SQR3, STM32F4_SQ5_MASK, STM32F4_SQ5_SHIFT },
>>> + { STM32F4_ADCX_SQR3, STM32F4_SQ6_MASK, STM32F4_SQ6_SHIFT },
>>> + { STM32F4_ADCX_SQR2, STM32F4_SQ7_MASK, STM32F4_SQ7_SHIFT },
>>> + { STM32F4_ADCX_SQR2, STM32F4_SQ8_MASK, STM32F4_SQ8_SHIFT },
>>> + { STM32F4_ADCX_SQR2, STM32F4_SQ9_MASK, STM32F4_SQ9_SHIFT },
>>> + { STM32F4_ADCX_SQR2, STM32F4_SQ10_MASK, STM32F4_SQ10_SHIFT },
>>> + { STM32F4_ADCX_SQR2, STM32F4_SQ11_MASK, STM32F4_SQ11_SHIFT },
>>> + { STM32F4_ADCX_SQR2, STM32F4_SQ12_MASK, STM32F4_SQ12_SHIFT },
>>> + { STM32F4_ADCX_SQR1, STM32F4_SQ13_MASK, STM32F4_SQ13_SHIFT },
>>> + { STM32F4_ADCX_SQR1, STM32F4_SQ14_MASK, STM32F4_SQ14_SHIFT },
>>> + { STM32F4_ADCX_SQR1, STM32F4_SQ15_MASK, STM32F4_SQ15_SHIFT },
>>> + { STM32F4_ADCX_SQR1, STM32F4_SQ16_MASK, STM32F4_SQ16_SHIFT },
>>> +};
>>> +
>>> +/**
>>> + * stm32f4_jsqr_reg - describe injected sequence register:
>>> + * - JL: injected sequence len
>>> + * - JSQ4..SQ1: sequence entries
>>> + * When JL == 3, ADC converts JSQ1, JSQ2, JSQ3, JSQ4
>>> + * When JL == 2, ADC converts JSQ2, JSQ3, JSQ4
>>> + * When JL == 1, ADC converts JSQ3, JSQ4
>>> + * When JL == 0, ADC converts JSQ4
>>> + */
>>> +static const struct stm32_adc_regs stm32f4_jsqr_reg[STM32_ADC_MAX_JSQ + 1] = {
>>> + /* JL: len bit field description to be kept as first element */
>>> + {STM32F4_ADCX_JSQR, STM32F4_JL_MASK, STM32F4_JL_SHIFT},
>>> + /* JSQ4..JSQ1 registers & bit fields */
>>> + {STM32F4_ADCX_JSQR, STM32F4_JSQ4_MASK, STM32F4_JSQ4_SHIFT},
>>> + {STM32F4_ADCX_JSQR, STM32F4_JSQ3_MASK, STM32F4_JSQ3_SHIFT},
>>> + {STM32F4_ADCX_JSQR, STM32F4_JSQ2_MASK, STM32F4_JSQ2_SHIFT},
>>> + {STM32F4_ADCX_JSQR, STM32F4_JSQ1_MASK, STM32F4_JSQ1_SHIFT},
>>> +};
>>> +
>>> +static const struct stm32_adc_trig_reginfo stm32f4_adc_trig_reginfo = {
>>> + .reg = STM32F4_ADCX_CR2,
>>> + .exten_mask = STM32F4_EXTEN_MASK,
>>> + .exten_shift = STM32F4_EXTEN_SHIFT,
>>> + .extsel_mask = STM32F4_EXTSEL_MASK,
>>> + .extsel_shift = STM32F4_EXTSEL_SHIFT,
>>> +};
>>> +
>>> +static const struct stm32_adc_trig_reginfo stm32f4_adc_jtrig_reginfo = {
>>> + .reg = STM32F4_ADCX_CR2,
>>> + .exten_mask = STM32F4_JEXTEN_MASK,
>>> + .exten_shift = STM32F4_JEXTEN_SHIFT,
>>> + .extsel_mask = STM32F4_JEXTSEL_MASK,
>>> + .extsel_shift = STM32F4_JEXTSEL_SHIFT,
>>> +};
>>> +
>>> +static const struct stm32_adc_reginfo stm32f4_adc_reginfo = {
>>> + .isr = STM32F4_ADCX_SR,
>>> + .eoc = STM32F4_EOC,
>>> + .jeoc = STM32F4_JEOC,
>>> + .ier = STM32F4_ADCX_CR1,
>>> + .eocie = STM32F4_EOCIE,
>>> + .jeocie = STM32F4_JEOCIE,
>>> + .dr = STM32F4_ADCX_DR,
>>> + .jdr = {
>>> + STM32F4_ADCX_JDR1,
>>> + STM32F4_ADCX_JDR2,
>>> + STM32F4_ADCX_JDR3,
>>> + STM32F4_ADCX_JDR4,
>>> + },
>>> + .sqr_regs = stm32f4_sqr_regs,
>>> + .jsqr_reg = stm32f4_jsqr_reg,
>>> + .trig_reginfo = &stm32f4_adc_trig_reginfo,
>>> + .jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
>>> +};
>>> +
>>> +static bool stm32f4_adc_is_started(struct stm32_adc *adc)
>>> +{
>>> + u32 val = stm32_adc_readl(adc, STM32F4_ADCX_CR2) & STM32F4_ADON;
>>> +
>>> + return !!val;
>>> +}
>>> +
>>> +static bool stm32f4_adc_regular_started(struct stm32_adc *adc)
>>> +{
>>> + u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_STRT;
>>> +
>>> + return !!val;
>>> +}
>>> +
>>> +static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
>>> +{
>>> + u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_JSTRT;
>>> +
>>> + return !!val;
>>> +}
>>> +
>>> +/**
>>> + * stm32f4_adc_start_conv() - Start regular or injected conversions
>>> + * @adc: stm32 adc instance
>>> + *
>>> + * Start single conversions for regular or injected channels.
>>> + */
>>> +static int stm32f4_adc_start_conv(struct stm32_adc *adc)
>>> +{
>>> + u32 trig_msk, start_msk;
>>> +
>>> + dev_dbg(adc->common->dev, "%s %s\n", __func__,
>>> + adc->injected ? "injected" : "regular");
>>> +
>>> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>>> +
>>> + if (!stm32f4_adc_is_started(adc)) {
>>> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
>>> + STM32F4_EOCS | STM32F4_ADON);
>>> +
>>> + /* Wait for Power-up time (tSTAB from datasheet) */
>>> + usleep_range(2, 3);
>>> + }
>>> +
>>> + if (adc->injected) {
>>> + trig_msk = STM32F4_JEXTEN_MASK;
>>> + start_msk = STM32F4_JSWSTART;
>>> + } else {
>>> + trig_msk = STM32F4_EXTEN_MASK;
>>> + start_msk = STM32F4_SWSTART;
>>> + }
>>> +
>>> + /* Software start ? (e.g. trigger detection disabled ?) */
>>> + if (!(stm32_adc_readl(adc, STM32F4_ADCX_CR2) & trig_msk))
>>> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, start_msk);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
>>> +{
>>> + u32 val;
>>> +
>>> + dev_dbg(adc->common->dev, "%s %s\n", __func__,
>>> + adc->injected ? "injected" : "regular");
>>> +
>>> + /* First disable trigger for either regular or injected channels */
>>> + if (adc->injected) {
>>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_JEXTEN_MASK);
>>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_JSTRT);
>>> + } else {
>>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
>>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
>>> + }
>>> +
>>> + /* Disable adc when all triggered conversion have been disabled */
>>> + val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
>>> + val &= STM32F4_EXTEN_MASK | STM32F4_JEXTEN_MASK;
>>> + if (!val) {
>>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/* ADC internal common clock prescaler division ratios */
>>> +static int stm32f4_pclk_div[] = {2, 4, 6, 8};
>>> +
>>> +/**
>>> + * stm32f4_adc_clk_sel() - Select ADC common clock prescaler
>>> + * @adc: stm32 adc instance
>>> + * Select clock prescaler used for analog conversions.
>>> + */
>>> +static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
>>> +{
>>> + struct stm32_adc_common *common = adc->common;
>>> + unsigned long rate;
>>> + u32 val;
>>> + int i;
>>> +
>>> + /* Common prescaler is set only once, when 1st ADC instance starts */
>>> + list_for_each_entry(adc, &common->adc_list, adc_list)
>>> + if (stm32f4_adc_is_started(adc))
>>> + return 0;
>>> +
>>> + rate = clk_get_rate(common->aclk);
>>> + for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
>>> + if ((rate / stm32f4_pclk_div[i]) <=
>>> + common->data->max_clock_rate)
>>> + break;
>>> + }
>>> + if (i >= ARRAY_SIZE(stm32f4_pclk_div))
>>> + return -EINVAL;
>>> +
>>> + val = stm32_adc_common_readl(common, STM32F4_ADC_CCR);
>>> + val &= ~STM32F4_ADC_ADCPRE_MASK;
>>> + val |= i << STM32F4_ADC_ADCPRE_SHIFT;
>>> + stm32_adc_common_writel(common, STM32F4_ADC_CCR, val);
>>> +
>>> + dev_dbg(common->dev, "Using analog clock source at %ld kHz\n",
>>> + rate / (stm32f4_pclk_div[i] * 1000));
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct stm32_adc_ops stm32f4_adc_ops = {
>>> + .adc_info = stm32f4_adc_info,
>>> + .ext_triggers = stm32f4_adc_ext_triggers,
>>> + .jext_triggers = stm32f4_adc_jext_triggers,
>>> + .adc_reginfo = &stm32f4_adc_reginfo,
>>> + .highres = 12,
>>> + .max_clock_rate = 36000000,
>>> + .clk_sel = stm32f4_adc_clk_sel,
>>> + .start_conv = stm32f4_adc_start_conv,
>>> + .stop_conv = stm32f4_adc_stop_conv,
>>> + .is_started = stm32f4_adc_is_started,
>>> + .regular_started = stm32f4_adc_regular_started,
>>> + .injected_started = stm32f4_adc_injected_started,
>>> +};
>>> +
>>> +static const struct of_device_id stm32f4_adc_of_match[] = {
>>> + { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_ops},
>>> + {},
>>> +};
>>> +MODULE_DEVICE_TABLE(of, stm32f4_adc_of_match);
>>> +
>>> +static struct platform_driver stm32f4_adc_driver = {
>>> + .probe = stm32_adc_probe,
>>> + .remove = stm32_adc_remove,
>>> + .driver = {
>>> + .name = "stm32f4-adc",
>>> + .of_match_table = stm32f4_adc_of_match,
>>> + },
>>> +};
>>> +
>>> +module_platform_driver(stm32f4_adc_driver);
>>> +
>>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>>> +MODULE_DESCRIPTION("STMicroelectronics STM32F4 ADC driver");
>>> +MODULE_LICENSE("GPL v2");
>>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v4] ARM: w90x900: let clk_disable() return immediately if clk is NULL
From: Masahiro Yamada @ 2016-11-05 15:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474221559-21269-1-git-send-email-yamada.masahiro@socionext.com>
Hi Wan,
This patch was acked by you long before,
but it has not been pulled-in yet for some reasons.
Now the patch was split per platform.
So, could you apply this patch to your tree, please?
2016-09-19 2:59 GMT+09:00 Masahiro Yamada <yamada.masahiro@socionext.com>:
> In many of clk_disable() implementations, it is a no-op for a NULL
> pointer input, but this is one of the exceptions.
>
> Making it treewide consistent will allow clock consumers to call
> clk_disable() without NULL pointer check.
>
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> Acked-by: Wan Zongshun <mcuos.com@gmail.com>
> ---
>
> Changes in v4:
> - Split into per-arch patches
>
> Changes in v3:
> - Return only when clk is NULL. Do not take care of error pointer.
>
> Changes in v2:
> - Rebase on Linux 4.6-rc1
>
> arch/arm/mach-w90x900/clock.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/arch/arm/mach-w90x900/clock.c b/arch/arm/mach-w90x900/clock.c
> index 2c371ff..ac6fd1a 100644
> --- a/arch/arm/mach-w90x900/clock.c
> +++ b/arch/arm/mach-w90x900/clock.c
> @@ -46,6 +46,9 @@ void clk_disable(struct clk *clk)
> {
> unsigned long flags;
>
> + if (!clk)
> + return;
> +
> WARN_ON(clk->enabled == 0);
>
> spin_lock_irqsave(&clocks_lock, flags);
> --
> 1.9.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--
Best Regards
Masahiro Yamada
^ permalink raw reply
* Low network throughput on i.MX28
From: Koul, Vinod @ 2016-11-05 15:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478351681.353.5.camel@embedded.rocks>
On Sat, 2016-11-05 at 14:14 +0100, J?rg Krause wrote:
> On Sat, 2016-11-05 at 12:39 +0000, Koul, Vinod wrote:
> >
> > On Sat, 2016-11-05 at 13:06 +0100, J?rg Krause wrote:
> > >
> > > @ Vinod
> > > In short, I noticed poor performance in the SSP2 (MMC/SD/SDIO)
> > > interface on a custom i.MX28 board with a wifi chip attached.
> > > Comparing
> > > the bandwith with iperf I get >20Mbits/sec on the vendor kernel
> > > and
> > > <5Mbits/sec on the mainline kernel. I am trying to investigate
> > > what
> > > the
> > > bottleneck is.
> > is this imx-dma or imx-sdma..
> >
> > >
> > >
> > > @ Stefan, all
> > > My understanding is that the tasklet in this case is responsible
> > > for
> > > reading the response registers of the DMA controller and return
> > > the
> > > response to the MMC host driver.
> > >
> > > The vendor kernel does this in the interrupt routine of mxs-mmc by
> > > issueing a complete whereas the mainline kernel does this in the
> > > interrupt routine in mxs-dma by scheduling the tasklet.
> > Is vendor kernel using dmaengine APIs or not?
> It's this engine [1].
>
> [1] http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/tree/a
> r
> ch/arm/plat-mxs/dmaengine.c?h=imx_2.6.35_1.1.0
Thanks for info, this looks okay.
First can you confirm that register configuration for DMA transaction is
same in both cases.
Second, looking at the driver I see that interrupt handler is not
pushing next descriptor. Also the tasklet is doing callback action and
not pushing any descriptors, did I miss anything in this?
For good dma throughput, you should have multiple dma transactions
queued up and submitted as fast as possible. Can you check if this is
being done.?
We need to minimize/eliminate the delay between two transactions. This
can be done in SW or HW based on support from HW. If HW supports
chaining of descriptors then next transaction which is given to
dmaengine driver should be appended at the end. If not submit the
descriptor to hw immediately on interrupt.?
For good example of latter please look at?drivers/dma/sa11x0-dma.c
HTH
--
~Vinod
^ permalink raw reply
* [PATCH] ARM: S3C24XX: Add DMA slave maps for remaining s3c24xx SoCs
From: Krzysztof Kozlowski @ 2016-11-05 15:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478252753-8231-1-git-send-email-s.nawrocki@samsung.com>
On Fri, Nov 04, 2016 at 10:45:53AM +0100, Sylwester Nawrocki wrote:
> This patch adds DMA slave map tables for the remaining s3c24xx
> SoC types so the whole platform can be switched to the new API.
> A few devices for which there was no DMA support with current
> code are omitted from the tables.
>
> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
> ---
> arch/arm/mach-s3c24xx/common.c | 76 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 76 insertions(+)
Thanks, applied.
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH v2 2/9] drm/sun4i: support A33 tcon
From: Chen-Yu Tsai @ 2016-11-05 15:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160906144620.6322-3-maxime.ripard@free-electrons.com>
Hi,
On Tue, Sep 6, 2016 at 10:46 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> The A33 has a significantly different pipeline, with components that differ
> too.
>
> Make sure we had compatible for them.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
> Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 7 ++++++-
> drivers/gpu/drm/sun4i/sun4i_backend.c | 1 +
> drivers/gpu/drm/sun4i/sun4i_drv.c | 8 +++++---
> drivers/gpu/drm/sun4i/sun4i_tcon.c | 8 +++++++-
> 4 files changed, 19 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
> index df8f4aeefe4c..bd3136a5cba5 100644
> --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
> +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
> @@ -26,7 +26,9 @@ TCON
> The TCON acts as a timing controller for RGB, LVDS and TV interfaces.
>
> Required properties:
> - - compatible: value should be "allwinner,sun5i-a13-tcon".
> + - compatible: value must be either:
> + * allwinner,sun5i-a13-tcon
> + * allwinner,sun8i-a33-tcon
> - reg: base address and size of memory-mapped region
> - interrupts: interrupt associated to this IP
> - clocks: phandles to the clocks feeding the TCON. Three are needed:
> @@ -59,6 +61,7 @@ system.
> Required properties:
> - compatible: value must be one of:
> * allwinner,sun5i-a13-display-backend
> + * allwinner,sun8i-a33-display-backend
> - reg: base address and size of the memory-mapped region.
> - clocks: phandles to the clocks feeding the frontend and backend
> * ahb: the backend interface clock
> @@ -80,6 +83,7 @@ deinterlacing and color space conversion.
> Required properties:
> - compatible: value must be one of:
> * allwinner,sun5i-a13-display-frontend
> + * allwinner,sun8i-a33-display-frontend
I just looked at the A23. It seems it's the same display frontend as the A33.
Should we change the compatible string to a23 while it's still in RC?
The backend is probably different. The A33 only claims to support 2048x2048
layers, while the A23 claims to support 8192x8192 layers.
Regards
ChenYu
> - reg: base address and size of the memory-mapped region.
> - interrupts: interrupt associated to this IP
> - clocks: phandles to the clocks feeding the frontend and backend
> @@ -104,6 +108,7 @@ extra node.
> Required properties:
> - compatible: value must be one of:
> * allwinner,sun5i-a13-display-engine
> + * allwinner,sun8i-a33-display-engine
>
> - allwinner,pipelines: list of phandle to the display engine
> frontends available.
> diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c
> index 3ab560450a82..9bfd2e45fceb 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_backend.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
> @@ -345,6 +345,7 @@ static int sun4i_backend_remove(struct platform_device *pdev)
>
> static const struct of_device_id sun4i_backend_of_table[] = {
> { .compatible = "allwinner,sun5i-a13-display-backend" },
> + { .compatible = "allwinner,sun8i-a33-display-backend" },
> { }
> };
> MODULE_DEVICE_TABLE(of, sun4i_backend_of_table);
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
> index 942f62e2441c..c4d03c1b6db8 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
> @@ -199,13 +199,14 @@ static const struct component_master_ops sun4i_drv_master_ops = {
>
> static bool sun4i_drv_node_is_frontend(struct device_node *node)
> {
> - return of_device_is_compatible(node,
> - "allwinner,sun5i-a13-display-frontend");
> + return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") ||
> + of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend");
> }
>
> static bool sun4i_drv_node_is_tcon(struct device_node *node)
> {
> - return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon");
> + return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") ||
> + of_device_is_compatible(node, "allwinner,sun8i-a33-tcon");
> }
>
> static int compare_of(struct device *dev, void *data)
> @@ -320,6 +321,7 @@ static int sun4i_drv_remove(struct platform_device *pdev)
>
> static const struct of_device_id sun4i_drv_of_table[] = {
> { .compatible = "allwinner,sun5i-a13-display-engine" },
> + { .compatible = "allwinner,sun8i-a33-display-engine" },
> { }
> };
> MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
> diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
> index fde6af1230d2..cadacb517f95 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
> @@ -488,8 +488,13 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
> tcon->drm = drm;
> tcon->dev = dev;
>
> - if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon"))
> + if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon")) {
> tcon->has_mux = true;
> + tcon->has_channel_1 = true;
> + } else {
> + tcon->has_mux = false;
> + tcon->has_channel_1 = false;
> + }
>
> tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
> if (IS_ERR(tcon->lcd_rst)) {
> @@ -585,6 +590,7 @@ static int sun4i_tcon_remove(struct platform_device *pdev)
>
> static const struct of_device_id sun4i_tcon_of_table[] = {
> { .compatible = "allwinner,sun5i-a13-tcon" },
> + { .compatible = "allwinner,sun8i-a33-tcon" },
> { }
> };
> MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
> --
> 2.9.3
>
^ 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