* [PATCH] cpufreq: s3c64xx: remove incorrect __init annotation
From: Krzysztof Kozlowski @ 2016-12-16 16:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161216090625.2235668-1-arnd@arndb.de>
On Fri, Dec 16, 2016 at 10:06:15AM +0100, Arnd Bergmann wrote:
> s3c64xx_cpufreq_config_regulator is incorrectly annotated
> as __init, since the caller is also not init:
>
> WARNING: vmlinux.o(.text+0x92fe1c): Section mismatch in reference from the function s3c64xx_cpufreq_driver_init() to the function .init.text:s3c64xx_cpufreq_config_regulator()
>
> With modern gcc versions, the function gets inline, so we don't
> see the warning, this only happens with gcc-4.6 and older.
>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> drivers/cpufreq/s3c64xx-cpufreq.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH v3] arm64: mm: Fix NOMAP page initialization
From: Robert Richter @ 2016-12-16 16:54 UTC (permalink / raw)
To: linux-arm-kernel
On ThunderX systems with certain memory configurations we see the
following BUG_ON():
kernel BUG at mm/page_alloc.c:1848!
This happens for some configs with 64k page size enabled. The BUG_ON()
checks if start and end page of a memmap range belongs to the same
zone.
The BUG_ON() check fails if a memory zone contains NOMAP regions. In
this case the node information of those pages is not initialized. This
causes an inconsistency of the page links with wrong zone and node
information for that pages. NOMAP pages from node 1 still point to the
mem zone from node 0 and have the wrong nid assigned.
The reason for the mis-configuration is a change in pfn_valid() which
reports pages marked NOMAP as invalid:
68709f45385a arm64: only consider memblocks with NOMAP cleared for linear mapping
This causes pages marked as nomap being no longer reassigned to the
new zone in memmap_init_zone() by calling __init_single_pfn().
Fixing this by implementing an arm64 specific early_pfn_valid(). This
causes all pages of sections with memory including NOMAP ranges to be
initialized by __init_single_page() and ensures consistency of page
links to zone, node and section.
The HAVE_ARCH_PFN_VALID config option now requires an explicit
definiton of early_pfn_valid() in the same way as pfn_valid(). This
allows a customized implementation of early_pfn_valid() which
redirects to valid_section() for arm64. This is the same as for the
generic pfn_valid() implementation.
v3:
* Use valid_section() which is the same as the default pfn_valid()
implementation to initialize
* Added Ack for arm/ changes.
v2:
* Use pfn_present() instead of memblock_is_memory() to support also
non-memory NOMAP holes
Acked-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Robert Richter <rrichter@cavium.com>
---
arch/arm/include/asm/page.h | 1 +
arch/arm64/include/asm/page.h | 2 ++
arch/arm64/mm/init.c | 15 +++++++++++++++
include/linux/mmzone.h | 5 ++++-
4 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h
index 4355f0ec44d6..79761bd55f94 100644
--- a/arch/arm/include/asm/page.h
+++ b/arch/arm/include/asm/page.h
@@ -158,6 +158,7 @@ typedef struct page *pgtable_t;
#ifdef CONFIG_HAVE_ARCH_PFN_VALID
extern int pfn_valid(unsigned long);
+#define early_pfn_valid(pfn) pfn_valid(pfn)
#endif
#include <asm/memory.h>
diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h
index 8472c6def5ef..17ceb7435ded 100644
--- a/arch/arm64/include/asm/page.h
+++ b/arch/arm64/include/asm/page.h
@@ -49,6 +49,8 @@ typedef struct page *pgtable_t;
#ifdef CONFIG_HAVE_ARCH_PFN_VALID
extern int pfn_valid(unsigned long);
+extern int early_pfn_valid(unsigned long);
+#define early_pfn_valid early_pfn_valid
#endif
#include <asm/memory.h>
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 212c4d1e2f26..8ff62a7ff634 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -145,11 +145,26 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
#endif /* CONFIG_NUMA */
#ifdef CONFIG_HAVE_ARCH_PFN_VALID
+
int pfn_valid(unsigned long pfn)
{
return memblock_is_map_memory(pfn << PAGE_SHIFT);
}
EXPORT_SYMBOL(pfn_valid);
+
+/*
+ * This is the same as the generic pfn_valid() implementation. We use
+ * valid_section() here to make sure all pages of a section including
+ * NOMAP pages are initialized with __init_single_page().
+ */
+int early_pfn_valid(unsigned long pfn)
+{
+ if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS)
+ return 0;
+ return valid_section(__nr_to_section(pfn_to_section_nr(pfn)));
+}
+EXPORT_SYMBOL(early_pfn_valid);
+
#endif
#ifndef CONFIG_SPARSEMEM
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 0f088f3a2fed..bedcf8a95881 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -1170,12 +1170,16 @@ static inline struct mem_section *__pfn_to_section(unsigned long pfn)
}
#ifndef CONFIG_HAVE_ARCH_PFN_VALID
+
static inline int pfn_valid(unsigned long pfn)
{
if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS)
return 0;
return valid_section(__nr_to_section(pfn_to_section_nr(pfn)));
}
+
+#define early_pfn_valid(pfn) pfn_valid(pfn)
+
#endif
static inline int pfn_present(unsigned long pfn)
@@ -1200,7 +1204,6 @@ static inline int pfn_present(unsigned long pfn)
#define pfn_to_nid(pfn) (0)
#endif
-#define early_pfn_valid(pfn) pfn_valid(pfn)
void sparse_init(void);
#else
#define sparse_init() do {} while (0)
--
2.11.0
^ permalink raw reply related
* [PATCH] arm: dt: Initialize boot_command_line from CONFIG_CMDLINE in case DT does not provide /chosen/bootargs
From: Mark Rutland @ 2016-12-16 16:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161216154029.GF4920@atomide.com>
On Fri, Dec 16, 2016 at 07:40:30AM -0800, Tony Lindgren wrote:
> Yeah probaby best to add the empty chosen node to the ones that had
> skeleton.dtsi removed.
Yes please!
We should probably update the comment in skeleton.dtsi to be more
explicit. I had intended it to mean that chosen should always be
present in the dts, but I worded it poorly.
Thanks,
Mark.
^ permalink raw reply
* [PATCH 2/2] arm64: mm: enable CONFIG_HOLES_IN_ZONE for NUMA
From: Robert Richter @ 2016-12-16 17:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu8K+mokbjzM8EpTJoCp3XAKK1_Doq1Zx=A2CCWTT6FbYg@mail.gmail.com>
On 15.12.16 16:07:26, Ard Biesheuvel wrote:
> On 15 December 2016 at 15:39, Robert Richter <robert.richter@cavium.com> wrote:
> > I was going to do some measurements but my kernel crashes now with a
> > page fault in efi_rtc_probe():
> >
> > [ 21.663393] Unable to handle kernel paging request at virtual address 20251000
> > [ 21.663396] pgd = ffff000009090000
> > [ 21.663401] [20251000] *pgd=0000010ffff90003
> > [ 21.663402] , *pud=0000010ffff90003
> > [ 21.663404] , *pmd=0000000fdc030003
> > [ 21.663405] , *pte=00e8832000250707
> >
> > The sparsemem config requires the whole section to be initialized.
> > Your patches do not address this.
> >
>
> 96000047 is a third level translation fault, and the PTE address has
> RES0 bits set. I don't see how this is related to sparsemem, could you
> explain?
When initializing the whole section it works. Maybe it uncovers
another bug. Did not yet start debugging this.
>
> > On 14.12.16 09:11:47, Ard Biesheuvel wrote:
> >> +config HOLES_IN_ZONE
> >> + def_bool y
> >> + depends on NUMA
> >
> > This enables pfn_valid_within() for arm64 and causes the check for
> > each page of a section. The arm64 implementation of pfn_valid() is
> > already expensive (traversing memblock areas). Now, this is increased
> > by a factor of 2^18 for 4k page size (16384 for 64k). We need to
> > initialize the whole section to avoid that.
> >
>
> I know that. But if you want something for -stable, we should have
> something that is correct first, and only then care about the
> performance hit (if there is one)
I would prefer to check for a performance penalty *before* we put it
into stable. There is nor risk at all with the patch I am proposing.
See: https://lkml.org/lkml/2016/12/16/412
-Robert
^ permalink raw reply
* [PATCH] omap2: mark omap_init_rng as __init
From: Tony Lindgren @ 2016-12-16 17:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161216090906.2313595-1-arnd@arndb.de>
* Arnd Bergmann <arnd@arndb.de> [161216 01:10]:
> I found this section mismatch when building with an older
> compiler release:
>
> WARNING: vmlinux.o(.text+0x3051c): Section mismatch in reference from the function omap_init_rng() to the function .init.text:omap_device_build()
>
> Obviously this one function should be __init as well. Normally
> we don't get a warning as the function gets inlined into its
> caller.
Acked-by: Tony Lindgren <tony@atomide.com>
^ permalink raw reply
* [PATCH 2/2] arm64: mm: enable CONFIG_HOLES_IN_ZONE for NUMA
From: Robert Richter @ 2016-12-16 17:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <125f3064-bbec-d923-ad9f-b2d152ee2c2d@linaro.org>
On 16.12.16 09:57:20, Hanjun Guo wrote:
> Hi Robert,
>
> On 2016/12/15 23:39, Robert Richter wrote:
> >I was going to do some measurements but my kernel crashes now with a
> >page fault in efi_rtc_probe():
> >
> >[ 21.663393] Unable to handle kernel paging request at virtual address 20251000
> >[ 21.663396] pgd = ffff000009090000
> >[ 21.663401] [20251000] *pgd=0000010ffff90003
> >[ 21.663402] , *pud=0000010ffff90003
> >[ 21.663404] , *pmd=0000000fdc030003
> >[ 21.663405] , *pte=00e8832000250707
> >
> >The sparsemem config requires the whole section to be initialized.
> >Your patches do not address this.
>
> This patch set is running properly on D05, both the boot and
> LTP MM stress test are ok, seems it's a different configuration
> of memory mappings in firmware, just a stupid question, which
> part is related to this problem, is it only the Reserved memory?
The problem are efi reserved regions that are no longer reserved but
marked as nomap pages. Those are excluded from page initialization
causing parts of a memory section not being initialized.
-Robert
^ permalink raw reply
* [PATCH] ARM: disallow ARM_THUMB for ARMv4 builds
From: Ard Biesheuvel @ 2016-12-16 17:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161216091457.2452987-1-arnd@arndb.de>
On 16 December 2016 at 09:14, Arnd Bergmann <arnd@arndb.de> wrote:
> With old compilers (gcc-4.3 and earlier), we run into a build error
> when CONFIG_ARM_THUMB is enabled in kernels that have both ARMv4 and
> ARMv4T/ARMv5 CPU support:
>
> arch/arm/kernel/entry-armv.S: Assembler messages:
> arch/arm/kernel/entry-armv.S:938: Error: selected processor does not support `bx lr' in ARM mode
> arch/arm/kernel/entry-armv.S:961: Error: selected processor does not support `bx lr' in ARM mode
> arch/arm/kernel/entry-armv.S:1004: Error: selected processor does not support `bx lr' in ARM mode
>
> The problem evidently is that 'bx' cannot work on the old CPUs, but
> the new ones have to use it whenever returning to THUMB user space.
>
Can't we use the old
tst lr, #1
moveq pc, lr
bx lr
trick? (where bx lr needs to be emitted as a plain opcode to hide it
from the assembler)
> This was discussed a while ago without a conclusion about what
> the proper patch should be to solve it, and came again up now when I
> experimented with old toolchain versions.
>
> This sidesteps the problem by declaring that we do not support
> the configuration and instead have to disable CONFIG_ARM_THUMB
> and not use THUMB user space with a kernel that supports the
> FA526 CPU, which is the only one that is allowed in a multiplatform
> configuration together with ARMv4T/ARMv5 anyway. This is not
> a regression because the configuration never worked anyway.
> The only platform affected by this is moxart, as no other ARMv4
> platforms are part of ARCH_MULTIPLATFORM.
>
> Cc: Jonas Jensen <jonas.jensen@gmail.com>
> Link: http://lkml.iu.edu/hypermail/linux/kernel/1404.1/00908.html
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> arch/arm/mm/Kconfig | 6 +-----
> 1 file changed, 1 insertion(+), 5 deletions(-)
>
> diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
> index 5d4920c069f2..f7b7aa37964f 100644
> --- a/arch/arm/mm/Kconfig
> +++ b/arch/arm/mm/Kconfig
> @@ -655,11 +655,7 @@ config ARCH_DMA_ADDR_T_64BIT
>
> config ARM_THUMB
> bool "Support Thumb user binaries" if !CPU_THUMBONLY
> - depends on CPU_ARM720T || CPU_ARM740T || CPU_ARM920T || CPU_ARM922T || \
> - CPU_ARM925T || CPU_ARM926T || CPU_ARM940T || CPU_ARM946E || \
> - CPU_ARM1020 || CPU_ARM1020E || CPU_ARM1022 || CPU_ARM1026 || \
> - CPU_XSCALE || CPU_XSC3 || CPU_MOHAWK || CPU_V6 || CPU_V6K || \
> - CPU_V7 || CPU_FEROCEON || CPU_V7M
> + depends on !(CPU_32v3 || CPU_32v4)
> default y
> help
> Say Y if you want to include kernel support for running user space
> --
> 2.9.0
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH 3/3] arm64: dts: rockchip: add clk-480m for ehci and ohci of rk3399
From: Doug Anderson @ 2016-12-16 17:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <5853903D.8030605@rock-chips.com>
Hi,
On Thu, Dec 15, 2016 at 10:57 PM, Xing Zheng <zhengxing@rock-chips.com> wrote:
> Hi Heiko, Doug,
>
> On 2016?12?16? 02:18, Heiko Stuebner wrote:
>
> Am Donnerstag, 15. Dezember 2016, 08:34:09 CET schrieb Doug Anderson:
>
>
> I still need to digest all of the things that were added to this
> thread overnight, but nothing I've seen so far indicates that you need
> the post-gated clock. AKA I still think you need to redo your patch
> to replace:
>
> clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
> <&cru SCLK_USBPHY0_480M_SRC>;
>
> with:
>
> clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
> <&u2phy0>;
>
> Can you please comment on that?
>
> Also, with the change, the ehci will keep the clock (and thus the phy)
> always
> on. Does the phy-autosuspend even save anything now?
>
> In any case, could we make the clock-names entry sound nicer than
> usbphy0_480m
> please? bindings/usb/atmel-usb.txt calls its UTMI clock simply "usb_clk",
> but
> something like "utmi" should also work.
> While at it you could also fix up the other clock names to something like
> "host" and "arbiter" or so?.
>
>
> Heiko
>
>
> The usbphy related clock tress like this:
>
>
> Actually, at drivers/phy/phy-rockchip-inno-usb2.c, we can only
> enable/disable the master gate via GRF is PHY_PLL, not UTMI_CLK.
>
> And the naming style of the "hclk_host0" keep the name "hclk_host0" on the
> clcok tree diagram:
>
>
> Therefore, could we rename the clock name like this:
> ----
> for usb_host0_ehci and usb_host0_ohci:
> clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
> <&cru SCLK_U2PHY0>;
> clock-names = "hclk_host0", "hclk_host0_arb",
> "sclk_u2phy0";
>
> for usb_host1_ehci and usb_host1_ohci:
> clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>,
> <&cru SCLK_U2PHY1>;
> clock-names = "hclk_host1", "hclk_host1_arb",
> "sclk_u2phy1";
> ----
>
> BTW, the "arb" is an abbreviation for arbiter.
You don't specify what this new "SCLK_U2PHY0" ID is, so it's a little
hard for me to know what you're intending.
...however, I still don't see any reason why you can't just use the
solution I proposed. Specifying the clock as "<&u2phy0>" is the
correct thing to do. The input clock to the EHCI driver is exactly
the clock provided by the USB PHY with no gate in between (just as I
said). There is no reason to somehow buffer it by the cru. The cru
doesn't see this clock and has no reason to be involved.
> Thanks.
Note that there were many other comments on this thread besides mine.
Are you planning to address any of them?
-Doug
^ permalink raw reply
* [PATCH v8 1/8] soc: samsung: add exynos chipid driver support
From: Krzysztof Kozlowski @ 2016-12-16 17:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481375323-29724-2-git-send-email-pankaj.dubey@samsung.com>
On Sat, Dec 10, 2016 at 06:38:36PM +0530, Pankaj Dubey wrote:
> 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>
> [m.szyprowski: for suggestion and code snippet of product_id_to_soc_id]
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
> drivers/soc/samsung/Kconfig | 5 ++
> drivers/soc/samsung/Makefile | 1 +
> drivers/soc/samsung/exynos-chipid.c | 116 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 122 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)
1. Why this can be compile tested only on ARM architectures?
2. Don't you need also SOC_BUS?
> + 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
Please put it before EXYNOS_PMU, keeping this sorted alphabetical helps
avoiding conflicts of continuous edits.
> diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c
> new file mode 100644
> index 0000000..cf0128b
> --- /dev/null
> +++ b/drivers/soc/samsung/exynos-chipid.c
> @@ -0,0 +1,116 @@
> +/*
> + * 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 EXYNOS_SUBREV_MASK (0xF << 4)
> +#define EXYNOS_MAINREV_MASK (0xF << 0)
> +#define EXYNOS_REV_MASK (EXYNOS_SUBREV_MASK | EXYNOS_MAINREV_MASK)
> +
> +static const struct exynos_soc_id {
> + const char *name;
> + unsigned int id;
> + unsigned int mask;
> +} soc_ids[] = {
> + { "EXYNOS3250", 0xE3472000, 0xFFFFF000 },
> + { "EXYNOS4210", 0x43200000, 0xFFFE0000 },
> + { "EXYNOS4212", 0x43220000, 0xFFFE0000 },
> + { "EXYNOS4412", 0xE4412000, 0xFFFE0000 },
> + { "EXYNOS5250", 0x43520000, 0xFFFFF000 },
> + { "EXYNOS5260", 0xE5260000, 0xFFFFF000 },
> + { "EXYNOS5410", 0xE5410000, 0xFFFFF000 },
> + { "EXYNOS5420", 0xE5420000, 0xFFFFF000 },
> + { "EXYNOS5440", 0xE5440000, 0xFFFFF000 },
> + { "EXYNOS5800", 0xE5422000, 0xFFFFF000 },
> + { "EXYNOS7420", 0xE7420000, 0xFFFFF000 },
> + { "EXYNOS5433", 0xE5433000, 0xFFFFF000 },
> +};
> +
> +static const char * __init product_id_to_soc_id(unsigned int product_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(soc_ids); i++)
> + if ((product_id & soc_ids[i].mask) == soc_ids[i].id)
> + return soc_ids[i].name;
> + return "UNKNOWN";
> +}
> +
> +static const struct of_device_id of_exynos_chipid_ids[] = {
> + {
> + .compatible = "samsung,exynos4210-chipid",
> + },
> + {},
> +};
> +
> +/**
> + * exynos_chipid_early_init: Early chipid initialization
> + */
This comment is meaningless, it duplicates the name of function.
> +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);
You don't use the match here, so how about either
of_find_matching_node() or of_find_compatible_node()? The latter looks
better (less calls inside) and actually you want just check one
compatible field?
> + if (!np)
> + return -ENODEV;
> +
> + exynos_chipid_base = of_iomap(np, 0);
> +
> + if (!exynos_chipid_base)
> + return PTR_ERR(exynos_chipid_base);
> +
> + product_id = readl_relaxed(exynos_chipid_base);
Duplicated space before '='.
> + 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);
It wasn't allocated with *_const, so no need to free it.
Best regards,
Krzysztof
> + kfree(soc_dev_attr);
> + return PTR_ERR(soc_dev);
> + }
> +
> + return 0;
> +}
> +early_initcall(exynos_chipid_early_init);
> --
> 2.7.4
>
^ permalink raw reply
* [PATCH v8 3/8] ARM64: EXYNOS: enable exynos_chipid for ARCH_EXYNOS
From: Krzysztof Kozlowski @ 2016-12-16 17:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481375323-29724-4-git-send-email-pankaj.dubey@samsung.com>
On Sat, Dec 10, 2016 at 06:38:38PM +0530, Pankaj Dubey wrote:
> This patch enables exynos_chipid driver for ARCH_EXYNOS
> based SoC.
>
> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
> ---
> arch/arm64/Kconfig.platforms | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
> index 72f4eac..e6aa5c2 100644
> --- a/arch/arm64/Kconfig.platforms
> +++ b/arch/arm64/Kconfig.platforms
> @@ -53,6 +53,7 @@ config ARCH_BRCMSTB
>
> config ARCH_EXYNOS
> bool "ARMv8 based Samsung Exynos SoC family"
> + select EXYNOS_CHIPID
You allowed CHIPID only on ARMv7, not ARMv8.
Best regards,
Krzysztof
> select COMMON_CLK_SAMSUNG
> select HAVE_S3C2410_WATCHDOG if WATCHDOG
> select HAVE_S3C_RTC if RTC_CLASS
> --
> 2.7.4
>
^ permalink raw reply
* [PATCH v8 2/8] ARM: EXYNOS: enable exynos_chipid for ARCH_EXYNOS
From: Krzysztof Kozlowski @ 2016-12-16 18:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481375323-29724-3-git-send-email-pankaj.dubey@samsung.com>
On Sat, Dec 10, 2016 at 06:38:37PM +0530, Pankaj Dubey wrote:
> As now we have chipid driver to initialize SoC related information
> lets include it in build by default.
s/lets/let's/
Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
Best regards,
Krzysztof
>
> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
> ---
> arch/arm/mach-exynos/Kconfig | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
> index 0bb63b8..29065c5 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
> --
> 2.7.4
>
^ permalink raw reply
* [PATCH v8 4/8] ARM: EXYNOS: refactor firmware specific routines
From: Krzysztof Kozlowski @ 2016-12-16 18:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481375323-29724-5-git-send-email-pankaj.dubey@samsung.com>
On Sat, Dec 10, 2016 at 06:38:39PM +0530, Pankaj Dubey wrote:
> To remove dependency on soc_is_exynosMMMM macros and remove multiple
> checks for such macros lets refactor code in firmware.c file.
> SoC specific firmware_ops are separated and registered during
> exynos_firmware_init based on matching machine compatible.
>
> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
> ---
> arch/arm/mach-exynos/firmware.c | 100 ++++++++++++++++++++++++++++++----------
> 1 file changed, 75 insertions(+), 25 deletions(-)
>
> diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c
> index fd6da54..525fbd9 100644
> --- a/arch/arm/mach-exynos/firmware.c
> +++ b/arch/arm/mach-exynos/firmware.c
> @@ -35,6 +35,25 @@ static void exynos_save_cp15(void)
> : : "cc");
> }
>
> +static int exynos3250_do_idle(unsigned long mode)
> +{
> + switch (mode) {
> + case FW_DO_IDLE_AFTR:
> + writel_relaxed(virt_to_phys(exynos_cpu_resume_ns),
> + sysram_ns_base_addr + 0x24);
> + writel_relaxed(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20);
> + flush_cache_all();
> + exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE,
> + SMC_POWERSTATE_IDLE, 0);
> + exynos_smc(SMC_CMD_SHUTDOWN, OP_TYPE_CLUSTER,
> + SMC_POWERSTATE_IDLE, 0);
> + break;
> + case FW_DO_IDLE_SLEEP:
> + exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
> + }
> + return 0;
> +}
> +
> static int exynos_do_idle(unsigned long mode)
> {
> switch (mode) {
> @@ -44,14 +63,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()) {
> - flush_cache_all();
> - exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE,
> - SMC_POWERSTATE_IDLE, 0);
> - exynos_smc(SMC_CMD_SHUTDOWN, OP_TYPE_CLUSTER,
> - SMC_POWERSTATE_IDLE, 0);
> - } else
> - exynos_smc(SMC_CMD_CPU0AFTR, 0, 0, 0);
> + exynos_smc(SMC_CMD_CPU0AFTR, 0, 0, 0);
> break;
> case FW_DO_IDLE_SLEEP:
> exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
> @@ -59,28 +71,25 @@ static int exynos_do_idle(unsigned long mode)
> return 0;
> }
>
> -static int exynos_cpu_boot(int cpu)
> +static int exynos4412_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())
> - return 0;
> -
> - /*
> * The second parameter of SMC_CMD_CPU1BOOT command means CPU id.
> * But, Exynos4212 has only one secondary CPU so second parameter
> * isn't used for informing secure firmware about CPU id.
> */
> - if (soc_is_exynos4212())
> - cpu = 0;
> + cpu = 0;
Why are you clearing the cpu for Exynos4412? Was it tested on
Exynos4412?
> + exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0);
> + return 0;
> +}
>
> +static int exynos_cpu_boot(int cpu)
> +{
> exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0);
This will be executed on Exynos4212...
> return 0;
> }
>
> -static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
> +static int exynos4412_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
> {
> void __iomem *boot_reg;
>
> @@ -94,14 +103,24 @@ 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())
> - boot_reg += 4 * cpu;
> + boot_reg += 4 * cpu;
> + writel_relaxed(boot_addr, boot_reg);
> + return 0;
> +}
> +
> +static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
> +{
> + void __iomem *boot_reg;
>
> + if (!sysram_ns_base_addr)
> + return -ENODEV;
> +
> + boot_reg = sysram_ns_base_addr + 0x1c;
> writel_relaxed(boot_addr, boot_reg);
> return 0;
> }
>
> -static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
> +static int exynos4412_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
> {
> void __iomem *boot_reg;
>
> @@ -109,10 +128,19 @@ static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
> return -ENODEV;
>
> boot_reg = sysram_ns_base_addr + 0x1c;
> + boot_reg += 4 * cpu;
> + *boot_addr = readl_relaxed(boot_reg);
> + return 0;
> +}
> +
> +static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
> +{
> + void __iomem *boot_reg;
>
> - if (soc_is_exynos4412())
> - boot_reg += 4 * cpu;
> + if (!sysram_ns_base_addr)
> + return -ENODEV;
>
> + boot_reg = sysram_ns_base_addr + 0x1c;
> *boot_addr = readl_relaxed(boot_reg);
> return 0;
> }
> @@ -148,6 +176,23 @@ static int exynos_resume(void)
> return 0;
> }
>
> +static const struct firmware_ops exynos3250_firmware_ops = {
> + .do_idle = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos3250_do_idle : NULL,
> + .set_cpu_boot_addr = exynos_set_cpu_boot_addr,
> + .get_cpu_boot_addr = exynos_get_cpu_boot_addr,
You know that lack of cpu_boot() is not equivalent to previous
'return 0' code? Now -ENOSYS will be returned... which is not a problem
because return values for cpu_boot are ignored... just wondering whether
this was planned.
> + .suspend = IS_ENABLED(CONFIG_PM_SLEEP) ? exynos_suspend : NULL,
> + .resume = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_resume : NULL,
> +};
> +
> +static const struct firmware_ops exynos4412_firmware_ops = {
> + .do_idle = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_do_idle : NULL,
> + .set_cpu_boot_addr = exynos4412_set_cpu_boot_addr,
> + .get_cpu_boot_addr = exynos4412_get_cpu_boot_addr,
> + .cpu_boot = exynos4412_cpu_boot,
> + .suspend = IS_ENABLED(CONFIG_PM_SLEEP) ? exynos_suspend : NULL,
> + .resume = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_resume : NULL,
> +};
> +
> static const struct firmware_ops exynos_firmware_ops = {
> .do_idle = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_do_idle : NULL,
> .set_cpu_boot_addr = exynos_set_cpu_boot_addr,
> @@ -212,7 +257,12 @@ void __init exynos_firmware_init(void)
>
> pr_info("Running under secure firmware.\n");
>
> - register_firmware_ops(&exynos_firmware_ops);
> + if (of_machine_is_compatible("samsung,exynos3250"))
> + register_firmware_ops(&exynos3250_firmware_ops);
> + else if (of_machine_is_compatible("samsung,exynos4412"))
> + register_firmware_ops(&exynos4412_firmware_ops);
> + else
> + register_firmware_ops(&exynos_firmware_ops);
I prefer one register_firmware_ops() call, so something like:
const struct firmware_ops *ops;
if (...)
ops = &exynos3250_firmware_ops;
else if ()
...
register_firmware_ops(ops);
It is a matter of taste but for me it is more common pattern, looks more
readable and it reduces number of callers to register_firmware_ops() (so
it is easier to find them).
Krzysztof
>
> /*
> * Exynos 4 SoCs (based on Cortex A9 and equipped with L2C-310),
> --
> 2.7.4
>
^ permalink raw reply
* [PATCHv2 3/8] rtc: add STM32 RTC driver
From: Mathieu Poirier @ 2016-12-16 19:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481878257-29163-4-git-send-email-amelie.delaunay@st.com>
On Fri, Dec 16, 2016 at 09:50:52AM +0100, Amelie Delaunay wrote:
> This patch adds support for the STM32 RTC.
>
> Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
> ---
> drivers/rtc/Kconfig | 11 +
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-stm32.c | 776 ++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 788 insertions(+)
> create mode 100644 drivers/rtc/rtc-stm32.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index e859d14..11eb28a 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1706,6 +1706,17 @@ config RTC_DRV_PIC32
> This driver can also be built as a module. If so, the module
> will be called rtc-pic32
>
> +config RTC_DRV_STM32
> + tristate "STM32 RTC"
> + select REGMAP_MMIO
> + depends on ARCH_STM32 || COMPILE_TEST
> + help
> + If you say yes here you get support for the STM32 On-Chip
> + Real Time Clock.
> +
> + This driver can also be built as a module, if so, the module
> + will be called "rtc-stm32".
> +
> comment "HID Sensor RTC drivers"
>
> config RTC_DRV_HID_SENSOR_TIME
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 1ac694a..87bd9cc 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -144,6 +144,7 @@ obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
> obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
> obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
> obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
> +obj-$(CONFIG_RTC_DRV_STM32) += rtc-stm32.o
> obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
> obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
> obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
> diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c
> new file mode 100644
> index 0000000..6ce0f5a
> --- /dev/null
> +++ b/drivers/rtc/rtc-stm32.c
> @@ -0,0 +1,776 @@
> +/*
> + * Copyright (C) Amelie Delaunay 2016
> + * Author: Amelie Delaunay <amelie.delaunay@st.com>
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/bcd.h>
> +#include <linux/clk.h>
> +#include <linux/iopoll.h>
> +#include <linux/ioport.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/rtc.h>
> +#include <linux/spinlock.h>
> +
> +#define DRIVER_NAME "stm32_rtc"
> +
> +/* STM32 RTC registers */
> +#define STM32_RTC_TR 0x00
> +#define STM32_RTC_DR 0x04
> +#define STM32_RTC_CR 0x08
> +#define STM32_RTC_ISR 0x0C
> +#define STM32_RTC_PRER 0x10
> +#define STM32_RTC_ALRMAR 0x1C
> +#define STM32_RTC_WPR 0x24
> +
> +/* STM32_RTC_TR bit fields */
> +#define STM32_RTC_TR_SEC_SHIFT 0
> +#define STM32_RTC_TR_SEC GENMASK(6, 0)
> +#define STM32_RTC_TR_MIN_SHIFT 8
> +#define STM32_RTC_TR_MIN GENMASK(14, 8)
> +#define STM32_RTC_TR_HOUR_SHIFT 16
> +#define STM32_RTC_TR_HOUR GENMASK(21, 16)
> +
> +/* STM32_RTC_DR bit fields */
> +#define STM32_RTC_DR_DATE_SHIFT 0
> +#define STM32_RTC_DR_DATE GENMASK(5, 0)
> +#define STM32_RTC_DR_MONTH_SHIFT 8
> +#define STM32_RTC_DR_MONTH GENMASK(12, 8)
> +#define STM32_RTC_DR_WDAY_SHIFT 13
> +#define STM32_RTC_DR_WDAY GENMASK(15, 13)
> +#define STM32_RTC_DR_YEAR_SHIFT 16
> +#define STM32_RTC_DR_YEAR GENMASK(23, 16)
> +
> +/* STM32_RTC_CR bit fields */
> +#define STM32_RTC_CR_FMT BIT(6)
> +#define STM32_RTC_CR_ALRAE BIT(8)
> +#define STM32_RTC_CR_ALRAIE BIT(12)
> +
> +/* STM32_RTC_ISR bit fields */
> +#define STM32_RTC_ISR_ALRAWF BIT(0)
> +#define STM32_RTC_ISR_INITS BIT(4)
> +#define STM32_RTC_ISR_RSF BIT(5)
> +#define STM32_RTC_ISR_INITF BIT(6)
> +#define STM32_RTC_ISR_INIT BIT(7)
> +#define STM32_RTC_ISR_ALRAF BIT(8)
> +
> +/* STM32_RTC_PRER bit fields */
> +#define STM32_RTC_PRER_PRED_S_SHIFT 0
> +#define STM32_RTC_PRER_PRED_S GENMASK(14, 0)
> +#define STM32_RTC_PRER_PRED_A_SHIFT 16
> +#define STM32_RTC_PRER_PRED_A GENMASK(22, 16)
> +
> +/* STM32_RTC_ALRMAR and STM32_RTC_ALRMBR bit fields */
> +#define STM32_RTC_ALRMXR_SEC_SHIFT 0
> +#define STM32_RTC_ALRMXR_SEC GENMASK(6, 0)
> +#define STM32_RTC_ALRMXR_SEC_MASK BIT(7)
> +#define STM32_RTC_ALRMXR_MIN_SHIFT 8
> +#define STM32_RTC_ALRMXR_MIN GENMASK(14, 8)
> +#define STM32_RTC_ALRMXR_MIN_MASK BIT(15)
> +#define STM32_RTC_ALRMXR_HOUR_SHIFT 16
> +#define STM32_RTC_ALRMXR_HOUR GENMASK(21, 16)
> +#define STM32_RTC_ALRMXR_PM BIT(22)
> +#define STM32_RTC_ALRMXR_HOUR_MASK BIT(23)
> +#define STM32_RTC_ALRMXR_DATE_SHIFT 24
> +#define STM32_RTC_ALRMXR_DATE GENMASK(29, 24)
> +#define STM32_RTC_ALRMXR_WDSEL BIT(30)
> +#define STM32_RTC_ALRMXR_WDAY_SHIFT 24
> +#define STM32_RTC_ALRMXR_WDAY GENMASK(27, 24)
> +#define STM32_RTC_ALRMXR_DATE_MASK BIT(31)
> +
> +/* STM32_RTC_WPR key constants */
> +#define RTC_WPR_1ST_KEY 0xCA
> +#define RTC_WPR_2ND_KEY 0x53
> +#define RTC_WPR_WRONG_KEY 0xFF
> +
> +/*
> + * RTC registers are protected agains parasitic write access.
> + * PWR_CR_DBP bit must be set to enable write access to RTC registers.
> + */
> +/* STM32_PWR_CR */
> +#define PWR_CR 0x00
> +/* STM32_PWR_CR bit field */
> +#define PWR_CR_DBP BIT(8)
> +
> +static struct regmap *dbp;
> +
> +struct stm32_rtc {
> + struct rtc_device *rtc_dev;
> + void __iomem *base;
> + struct clk *ck_rtc;
> + spinlock_t lock; /* Protects registers accesses */
> + int irq_alarm;
> +};
> +
> +static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc)
> +{
> + writel_relaxed(RTC_WPR_1ST_KEY, rtc->base + STM32_RTC_WPR);
> + writel_relaxed(RTC_WPR_2ND_KEY, rtc->base + STM32_RTC_WPR);
> +}
> +
> +static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc)
> +{
> + writel_relaxed(RTC_WPR_WRONG_KEY, rtc->base + STM32_RTC_WPR);
> +}
> +
> +static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc)
> +{
> + unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
> +
> + if (!(isr & STM32_RTC_ISR_INITF)) {
> + isr |= STM32_RTC_ISR_INIT;
> + writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
> +
> + /*
> + * It takes around 2 ck_rtc clock cycles to enter in
> + * initialization phase mode (and have INITF flag set). As
> + * slowest ck_rtc frequency may be 32kHz and highest should be
> + * 1MHz, we poll every 10 us with a timeout of 100ms.
> + */
> + return readl_relaxed_poll_timeout_atomic(
> + rtc->base + STM32_RTC_ISR,
> + isr, (isr & STM32_RTC_ISR_INITF),
> + 10, 100000);
> + }
> +
> + return 0;
> +}
> +
> +static void stm32_rtc_exit_init_mode(struct stm32_rtc *rtc)
> +{
> + unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
> +
> + isr &= ~STM32_RTC_ISR_INIT;
> + writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
> +}
> +
> +static int stm32_rtc_wait_sync(struct stm32_rtc *rtc)
> +{
> + unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
> +
> + isr &= ~STM32_RTC_ISR_RSF;
> + writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
> +
> + /*
> + * Wait for RSF to be set to ensure the calendar registers are
> + * synchronised, it takes around 2 ck_rtc clock cycles
> + */
> + return readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
> + isr,
> + (isr & STM32_RTC_ISR_RSF),
> + 10, 100000);
> +}
> +
> +static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id)
> +{
> + struct stm32_rtc *rtc = (struct stm32_rtc *)dev_id;
> + unsigned int isr, cr;
> +
> + mutex_lock(&rtc->rtc_dev->ops_lock);
> +
> + isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
> +
> + if ((isr & STM32_RTC_ISR_ALRAF) &&
> + (cr & STM32_RTC_CR_ALRAIE)) {
> + /* Alarm A flag - Alarm interrupt */
> + dev_dbg(&rtc->rtc_dev->dev, "Alarm occurred\n");
> +
> + /* Pass event to the kernel */
> + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
> +
> + /* Clear event flag, otherwise new events won't be received */
> + writel_relaxed(isr & ~STM32_RTC_ISR_ALRAF,
> + rtc->base + STM32_RTC_ISR);
> + }
> +
> + mutex_unlock(&rtc->rtc_dev->ops_lock);
> +
> + return IRQ_HANDLED;
> +}
> +
> +/* Convert rtc_time structure from bin to bcd format */
> +static void tm2bcd(struct rtc_time *tm)
> +{
> + tm->tm_sec = bin2bcd(tm->tm_sec);
> + tm->tm_min = bin2bcd(tm->tm_min);
> + tm->tm_hour = bin2bcd(tm->tm_hour);
> +
> + tm->tm_mday = bin2bcd(tm->tm_mday);
> + tm->tm_mon = bin2bcd(tm->tm_mon + 1);
> + tm->tm_year = bin2bcd(tm->tm_year - 100);
> + /*
> + * Number of days since Sunday
> + * - on kernel side, 0=Sunday...6=Saturday
> + * - on rtc side, 0=invalid,1=Monday...7=Sunday
> + */
> + tm->tm_wday = (!tm->tm_wday) ? 7 : tm->tm_wday;
> +}
> +
> +/* Convert rtc_time structure from bcd to bin format */
> +static void bcd2tm(struct rtc_time *tm)
> +{
> + tm->tm_sec = bcd2bin(tm->tm_sec);
> + tm->tm_min = bcd2bin(tm->tm_min);
> + tm->tm_hour = bcd2bin(tm->tm_hour);
> +
> + tm->tm_mday = bcd2bin(tm->tm_mday);
> + tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
> + tm->tm_year = bcd2bin(tm->tm_year) + 100;
> + /*
> + * Number of days since Sunday
> + * - on kernel side, 0=Sunday...6=Saturday
> + * - on rtc side, 0=invalid,1=Monday...7=Sunday
> + */
> + tm->tm_wday %= 7;
> +}
> +
> +static int stm32_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> + unsigned int tr, dr;
> + unsigned long irqflags;
> +
> + spin_lock_irqsave(&rtc->lock, irqflags);
> +
> + /* Time and Date in BCD format */
> + tr = readl_relaxed(rtc->base + STM32_RTC_TR);
> + dr = readl_relaxed(rtc->base + STM32_RTC_DR);
> +
> + spin_unlock_irqrestore(&rtc->lock, irqflags);
> +
> + tm->tm_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT;
> + tm->tm_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT;
> + tm->tm_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT;
> +
> + tm->tm_mday = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT;
> + tm->tm_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT;
> + tm->tm_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT;
> + tm->tm_wday = (dr & STM32_RTC_DR_WDAY) >> STM32_RTC_DR_WDAY_SHIFT;
> +
> + /* We don't report tm_yday and tm_isdst */
> +
> + bcd2tm(tm);
> +
> + if (rtc_valid_tm(tm) < 0) {
> + dev_err(dev, "%s: rtc_time is not valid.\n", __func__);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int stm32_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> + unsigned int tr, dr;
> + unsigned long irqflags;
> + int ret = 0;
> +
> + if (rtc_valid_tm(tm) < 0) {
> + dev_err(dev, "%s: rtc_time is not valid.\n", __func__);
> + return -EINVAL;
> + }
> +
> + tm2bcd(tm);
> +
> + /* Time in BCD format */
> + tr = ((tm->tm_sec << STM32_RTC_TR_SEC_SHIFT) & STM32_RTC_TR_SEC) |
> + ((tm->tm_min << STM32_RTC_TR_MIN_SHIFT) & STM32_RTC_TR_MIN) |
> + ((tm->tm_hour << STM32_RTC_TR_HOUR_SHIFT) & STM32_RTC_TR_HOUR);
> +
> + /* Date in BCD format */
> + dr = ((tm->tm_mday << STM32_RTC_DR_DATE_SHIFT) & STM32_RTC_DR_DATE) |
> + ((tm->tm_mon << STM32_RTC_DR_MONTH_SHIFT) & STM32_RTC_DR_MONTH) |
> + ((tm->tm_year << STM32_RTC_DR_YEAR_SHIFT) & STM32_RTC_DR_YEAR) |
> + ((tm->tm_wday << STM32_RTC_DR_WDAY_SHIFT) & STM32_RTC_DR_WDAY);
> +
> + spin_lock_irqsave(&rtc->lock, irqflags);
> +
> + stm32_rtc_wpr_unlock(rtc);
> +
> + ret = stm32_rtc_enter_init_mode(rtc);
> + if (ret) {
> + dev_err(dev, "Can't enter in init mode. Set time aborted.\n");
> + goto end;
> + }
> +
> + writel_relaxed(tr, rtc->base + STM32_RTC_TR);
> + writel_relaxed(dr, rtc->base + STM32_RTC_DR);
> +
> + stm32_rtc_exit_init_mode(rtc);
> +
> + ret = stm32_rtc_wait_sync(rtc);
> +end:
> + stm32_rtc_wpr_lock(rtc);
> +
> + spin_unlock_irqrestore(&rtc->lock, irqflags);
> +
> + return ret;
> +}
> +
> +static int stm32_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> + struct rtc_time *tm = &alrm->time;
> + unsigned int alrmar, cr, isr;
> + unsigned long irqflags;
> +
> + spin_lock_irqsave(&rtc->lock, irqflags);
> +
> + alrmar = readl_relaxed(rtc->base + STM32_RTC_ALRMAR);
> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
> + isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
> +
> + spin_unlock_irqrestore(&rtc->lock, irqflags);
> +
> + if (alrmar & STM32_RTC_ALRMXR_DATE_MASK) {
> + /*
> + * Date/day doesn't matter in Alarm comparison so alarm
> + * triggers every day
> + */
> + tm->tm_mday = -1;
> + tm->tm_wday = -1;
> + } else {
> + if (alrmar & STM32_RTC_ALRMXR_WDSEL) {
> + /* Alarm is set to a day of week */
> + tm->tm_mday = -1;
> + tm->tm_wday = (alrmar & STM32_RTC_ALRMXR_WDAY) >>
> + STM32_RTC_ALRMXR_WDAY_SHIFT;
> + tm->tm_wday %= 7;
> + } else {
> + /* Alarm is set to a day of month */
> + tm->tm_wday = -1;
> + tm->tm_mday = (alrmar & STM32_RTC_ALRMXR_DATE) >>
> + STM32_RTC_ALRMXR_DATE_SHIFT;
> + }
> + }
> +
> + if (alrmar & STM32_RTC_ALRMXR_HOUR_MASK) {
> + /* Hours don't matter in Alarm comparison */
> + tm->tm_hour = -1;
> + } else {
> + tm->tm_hour = (alrmar & STM32_RTC_ALRMXR_HOUR) >>
> + STM32_RTC_ALRMXR_HOUR_SHIFT;
> + if (alrmar & STM32_RTC_ALRMXR_PM)
> + tm->tm_hour += 12;
> + }
> +
> + if (alrmar & STM32_RTC_ALRMXR_MIN_MASK) {
> + /* Minutes don't matter in Alarm comparison */
> + tm->tm_min = -1;
> + } else {
> + tm->tm_min = (alrmar & STM32_RTC_ALRMXR_MIN) >>
> + STM32_RTC_ALRMXR_MIN_SHIFT;
> + }
> +
> + if (alrmar & STM32_RTC_ALRMXR_SEC_MASK) {
> + /* Seconds don't matter in Alarm comparison */
> + tm->tm_sec = -1;
> + } else {
> + tm->tm_sec = (alrmar & STM32_RTC_ALRMXR_SEC) >>
> + STM32_RTC_ALRMXR_SEC_SHIFT;
> + }
> +
> + bcd2tm(tm);
> +
> + alrm->enabled = (cr & STM32_RTC_CR_ALRAE) ? 1 : 0;
> + alrm->pending = (isr & STM32_RTC_ISR_ALRAF) ? 1 : 0;
> +
> + return 0;
> +}
> +
> +static int stm32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> + unsigned long irqflags;
> + unsigned int isr, cr;
> +
> + spin_lock_irqsave(&rtc->lock, irqflags);
> +
> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
> +
> + stm32_rtc_wpr_unlock(rtc);
> +
> + /* We expose Alarm A to the kernel */
> + if (enabled)
> + cr |= (STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
> + else
> + cr &= ~(STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
> + writel_relaxed(cr, rtc->base + STM32_RTC_CR);
> +
> + /* Clear event irqflags, otherwise new events won't be received */
> + isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
> + isr &= ~STM32_RTC_ISR_ALRAF;
> + writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
> +
> + stm32_rtc_wpr_lock(rtc);
> +
> + spin_unlock_irqrestore(&rtc->lock, irqflags);
> +
> + return 0;
> +}
> +
> +static int stm32_rtc_valid_alrm(struct stm32_rtc *rtc, struct rtc_time *tm)
> +{
> + unsigned int cur_day, cur_mon, cur_year, cur_hour, cur_min, cur_sec;
> + unsigned int dr = readl_relaxed(rtc->base + STM32_RTC_DR);
> + unsigned int tr = readl_relaxed(rtc->base + STM32_RTC_TR);
> +
> + cur_day = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT;
> + cur_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT;
> + cur_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT;
> + cur_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT;
> + cur_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT;
> + cur_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT;
> +
> + /*
> + * Assuming current date is M-D-Y H:M:S.
> + * RTC alarm can't be set on a specific month and year.
> + * So the valid alarm range is:
> + * M-D-Y H:M:S < alarm <= (M+1)-D-Y H:M:S
> + * with a specific case for December...
> + */
> + if ((((tm->tm_year > cur_year) &&
> + (tm->tm_mon == 0x1) && (cur_mon == 0x12)) ||
> + ((tm->tm_year == cur_year) &&
> + (tm->tm_mon <= cur_mon + 1))) &&
> + ((tm->tm_mday < cur_day) ||
> + ((tm->tm_mday == cur_day) &&
> + ((tm->tm_hour < cur_hour) ||
> + ((tm->tm_hour == cur_hour) && (tm->tm_min < cur_min)) ||
> + ((tm->tm_hour == cur_hour) && (tm->tm_min == cur_min) &&
> + (tm->tm_sec <= cur_sec))))))
> + return 0;
> +
> + return -EINVAL;
> +}
> +
> +static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> + struct rtc_time *tm = &alrm->time;
> + unsigned long irqflags;
> + unsigned int cr, isr, alrmar;
> + int ret = 0;
> +
> + if (rtc_valid_tm(tm)) {
> + dev_err(dev, "Alarm time not valid.\n");
> + return -EINVAL;
> + }
> +
> + tm2bcd(tm);
> +
> + /*
> + * RTC alarm can't be set on a specific date, unless this date is
> + * up to the same day of month next month.
> + */
> + if (stm32_rtc_valid_alrm(rtc, tm) < 0) {
> + dev_err(dev, "Alarm can be set only on upcoming month.\n");
> + return -EINVAL;
> + }
> +
> + spin_lock_irqsave(&rtc->lock, irqflags);
> +
> + stm32_rtc_wpr_unlock(rtc);
> +
> + /* Disable Alarm */
> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
> + cr &= ~STM32_RTC_CR_ALRAE;
> + writel_relaxed(cr, rtc->base + STM32_RTC_CR);
> +
> + /*
> + * Poll Alarm write flag to be sure that Alarm update is allowed: it
> + * takes around 2 ck_rtc clock cycles
> + */
> + ret = readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
> + isr,
> + (isr & STM32_RTC_ISR_ALRAWF),
> + 10, 100000);
> +
> + if (ret) {
> + dev_err(dev, "Alarm update not allowed\n");
> + goto end;
> + }
> +
> + alrmar = 0;
> + /* tm_year and tm_mon are not used because not supported by RTC */
> + alrmar |= (tm->tm_mday << STM32_RTC_ALRMXR_DATE_SHIFT) &
> + STM32_RTC_ALRMXR_DATE;
> + /* 24-hour format */
> + alrmar &= ~STM32_RTC_ALRMXR_PM;
> + alrmar |= (tm->tm_hour << STM32_RTC_ALRMXR_HOUR_SHIFT) &
> + STM32_RTC_ALRMXR_HOUR;
> + alrmar |= (tm->tm_min << STM32_RTC_ALRMXR_MIN_SHIFT) &
> + STM32_RTC_ALRMXR_MIN;
> + alrmar |= (tm->tm_sec << STM32_RTC_ALRMXR_SEC_SHIFT) &
> + STM32_RTC_ALRMXR_SEC;
All this work on alrmar is done while the spinlock is held. If I'm not
mistaking nothing prevents you from doing that processing before taking the
spinlock.
> +
> + /* Write to Alarm register */
> + writel_relaxed(alrmar, rtc->base + STM32_RTC_ALRMAR);
> +
> + if (alrm->enabled)
> + stm32_rtc_alarm_irq_enable(dev, 1);
> + else
> + stm32_rtc_alarm_irq_enable(dev, 0);
> +
> +end:
> + stm32_rtc_wpr_lock(rtc);
> +
> + spin_unlock_irqrestore(&rtc->lock, irqflags);
> +
> + return ret;
> +}
> +
> +static const struct rtc_class_ops stm32_rtc_ops = {
> + .read_time = stm32_rtc_read_time,
> + .set_time = stm32_rtc_set_time,
> + .read_alarm = stm32_rtc_read_alarm,
> + .set_alarm = stm32_rtc_set_alarm,
> + .alarm_irq_enable = stm32_rtc_alarm_irq_enable,
> +};
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id stm32_rtc_of_match[] = {
> + { .compatible = "st,stm32-rtc" },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, stm32_rtc_of_match);
> +#endif
> +
> +static int stm32_rtc_init(struct platform_device *pdev,
> + struct stm32_rtc *rtc)
> +{
> + unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr;
> + unsigned int rate;
> + unsigned long irqflags;
> + int ret = 0;
> +
> + rate = clk_get_rate(rtc->ck_rtc);
> +
> + /* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
> + pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
> + pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT;
> +
> + for (pred_a = pred_a_max; pred_a >= 0; pred_a--) {
> + pred_s = (rate / (pred_a + 1)) - 1;
> +
> + if (((pred_s + 1) * (pred_a + 1)) == rate)
> + break;
> + }
> +
> + /*
> + * Can't find a 1Hz, so give priority to RTC power consumption
> + * by choosing the higher possible value for prediv_a
> + */
> + if ((pred_s > pred_s_max) || (pred_a > pred_a_max)) {
> + pred_a = pred_a_max;
> + pred_s = (rate / (pred_a + 1)) - 1;
> +
> + dev_warn(&pdev->dev, "ck_rtc is %s\n",
> + (rate - ((pred_a + 1) * (pred_s + 1)) < 0) ?
> + "fast" : "slow");
> + }
> +
> + spin_lock_irqsave(&rtc->lock, irqflags);
> +
> + stm32_rtc_wpr_unlock(rtc);
> +
> + ret = stm32_rtc_enter_init_mode(rtc);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "Can't enter in init mode. Prescaler config failed.\n");
> + goto end;
> + }
> +
> + prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & STM32_RTC_PRER_PRED_S;
> + writel_relaxed(prer, rtc->base + STM32_RTC_PRER);
> + prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & STM32_RTC_PRER_PRED_A;
> + writel_relaxed(prer, rtc->base + STM32_RTC_PRER);
> +
> + /* Force 24h time format */
> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
> + cr &= ~STM32_RTC_CR_FMT;
> + writel_relaxed(cr, rtc->base + STM32_RTC_CR);
> +
> + stm32_rtc_exit_init_mode(rtc);
> +
> + ret = stm32_rtc_wait_sync(rtc);
> +end:
> + stm32_rtc_wpr_lock(rtc);
> +
> + spin_unlock_irqrestore(&rtc->lock, irqflags);
> +
> + return ret;
> +}
> +
> +static int stm32_rtc_probe(struct platform_device *pdev)
> +{
> + struct stm32_rtc *rtc;
> + struct resource *res;
> + int ret;
> +
> + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
> + if (!rtc)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + rtc->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(rtc->base))
> + return PTR_ERR(rtc->base);
> +
> + dbp = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "st,syscfg");
> + if (IS_ERR(dbp)) {
> + dev_err(&pdev->dev, "no st,syscfg\n");
> + return PTR_ERR(dbp);
> + }
> +
> + spin_lock_init(&rtc->lock);
> +
> + rtc->ck_rtc = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(rtc->ck_rtc)) {
> + dev_err(&pdev->dev, "no ck_rtc clock");
> + return PTR_ERR(rtc->ck_rtc);
> + }
> +
> + ret = clk_prepare_enable(rtc->ck_rtc);
> + if (ret)
> + return ret;
> +
> + regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP);
> +
> + /*
> + * After a system reset, RTC_ISR.INITS flag can be read to check if
> + * the calendar has been initalized or not. INITS flag is reset by a
> + * power-on reset (no vbat, no power-supply). It is not reset if
> + * ck_rtc parent clock has changed (so RTC prescalers need to be
> + * changed). That's why we cannot rely on this flag to know if RTC
> + * init has to be done.
> + */
> + ret = stm32_rtc_init(pdev, rtc);
> + if (ret)
> + goto err;
> +
> + rtc->irq_alarm = platform_get_irq(pdev, 0);
> + if (rtc->irq_alarm <= 0) {
> + dev_err(&pdev->dev, "no alarm irq\n");
> + ret = -ENOENT;
Function platform_get_irq() returns a wealth of error codes that are lost here.
Doing 'ret = rtc->irq_alarm;' would prevent that from happening.
> + goto err;
> + }
> +
> + platform_set_drvdata(pdev, rtc);
> +
> + ret = device_init_wakeup(&pdev->dev, true);
> + if (ret)
> + dev_warn(&pdev->dev,
> + "alarm won't be able to wake up the system");
> +
> + rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
> + &stm32_rtc_ops, THIS_MODULE);
> + if (IS_ERR(rtc->rtc_dev)) {
> + ret = PTR_ERR(rtc->rtc_dev);
> + dev_err(&pdev->dev, "rtc device registration failed, err=%d\n",
> + ret);
> + goto err;
> + }
> +
> + /* Handle RTC alarm interrupts */
> + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_alarm, NULL,
> + stm32_rtc_alarm_irq,
> + IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> + pdev->name, rtc);
> + if (ret) {
> + dev_err(&pdev->dev, "IRQ%d (alarm interrupt) already claimed\n",
> + rtc->irq_alarm);
> + goto err;
> + }
> +
> + /*
> + * If INITS flag is reset (calendar year field set to 0x00), calendar
> + * must be initialized
> + */
> + if (!(readl_relaxed(rtc->base + STM32_RTC_ISR) & STM32_RTC_ISR_INITS))
> + dev_warn(&pdev->dev, "Date/Time must be initialized\n");
> +
> + return 0;
> +err:
> + clk_disable_unprepare(rtc->ck_rtc);
> +
> + regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
> +
> + device_init_wakeup(&pdev->dev, false);
> +
> + return ret;
> +}
> +
> +static int __exit stm32_rtc_remove(struct platform_device *pdev)
> +{
> + struct stm32_rtc *rtc = platform_get_drvdata(pdev);
> + unsigned int cr;
> +
> + /* Disable interrupts */
> + stm32_rtc_wpr_unlock(rtc);
> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
> + cr &= ~STM32_RTC_CR_ALRAIE;
> + writel_relaxed(cr, rtc->base + STM32_RTC_CR);
> + stm32_rtc_wpr_lock(rtc);
> +
> + clk_disable_unprepare(rtc->ck_rtc);
> +
> + /* Enable backup domain write protection */
> + regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
> +
> + device_init_wakeup(&pdev->dev, false);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int stm32_rtc_suspend(struct device *dev)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> +
> + if (device_may_wakeup(dev))
> + return enable_irq_wake(rtc->irq_alarm);
> +
> + return 0;
> +}
> +
> +static int stm32_rtc_resume(struct device *dev)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> + int ret = 0;
> +
> + ret = stm32_rtc_wait_sync(rtc);
> + if (ret < 0)
> + return ret;
> +
> + if (device_may_wakeup(dev))
> + return disable_irq_wake(rtc->irq_alarm);
> +
> + return ret;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(stm32_rtc_pm_ops,
> + stm32_rtc_suspend, stm32_rtc_resume);
> +
> +static struct platform_driver stm32_rtc_driver = {
> + .probe = stm32_rtc_probe,
> + .remove = stm32_rtc_remove,
> + .driver = {
> + .name = DRIVER_NAME,
> + .pm = &stm32_rtc_pm_ops,
> + .of_match_table = stm32_rtc_of_match,
> + },
> +};
> +
> +module_platform_driver(stm32_rtc_driver);
> +
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> +MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.9.1
>
^ permalink raw reply
* [PATCH v2] ARM: dts: Add missing CPU frequencies for Exynos5422/5800
From: Javier Martinez Canillas @ 2016-12-16 19:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <10512254.nyUcL0zgTP@amdc3058>
Hello Bartlomiej,
On 12/15/2016 08:55 AM, Bartlomiej Zolnierkiewicz wrote:
> Add missing 2000MHz & 1900MHz OPPs (for A15 cores) and 1400MHz OPP
> (for A7 cores). Also update common Odroid-XU3 Lite/XU3/XU4 thermal
> cooling maps to account for new OPPs.
>
> Since new OPPs are not available on all Exynos5422/5800 boards modify
> dts files for Odroid-XU3 Lite (limited to 1.8 GHz / 1.3 GHz) & Peach
> Pi (limited to 2.0 GHz / 1.3 GHz) accordingly.
>
> Tested on Odroid-XU3 and XU3 Lite.
>
> Cc: Doug Anderson <dianders@chromium.org>
> Cc: Javier Martinez Canillas <javier@osg.samsung.com>
> Cc: Andreas Faerber <afaerber@suse.de>
> Cc: Thomas Abraham <thomas.ab@samsung.com>
> Cc: Ben Gamari <ben@smart-cactus.org>
> Cc: Arjun K V <arjun.kv@samsung.com>
> Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
> ---
> v2:
> - added comments about limitations of SoC revisions used by Odroid-XU3 Lite and
> Peach Pi boards (suggested by Javier)
> - removed redundant opp_a7_14 label
> - added Arjun to Cc:
>
> Javier, could you test it on Peach Pi board?
>
I've tested this patch on my Peach Pi by setting the CPUFreq governor to
performance and checking that the current scaling frequency was 2.0 GHz:
$ cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq
2000000
2000000
2000000
2000000
1400000
1400000
1400000
1400000
I ran the machine for hours and it didn't became unstable. Also installed
the stress [0] tool and setting the governor to ondemand, I executed:
$ stress --cpu 8
For about 30 minutes, the current frequencies were again the maximum and
had no issues.
Finally, I tested some devices that are under the INT rail (MFC and GSC)
by using the following GStreamer pipeline:
$ gst-launch-1.0 filesrc location=test-1080p.mp4 ! qtdemux ! h264parse ! \
v4l2video5dec ! v4l2video7convert capture-io-mode=dmabuf ! kmssink
Again, the system keeps working correctly. So I think it's safe to say:
Tested-by: Javier Martinez Canillas <javier@osg.samsung.com>
I can't provide a Reviewed-by since is still not clear to me if these OPP
values are correct or not due the ChromiumOS tree using higher voltages.
FWIW, the chip ID block information on my Exynos5800 Peach Pi is:
Exynos: CPU[EXYNOS5800] CPU_REV[0x1] PKG_ID[0x350c82f8] AUX_INFO[0x4d]
[0]: http://people.seas.harvard.edu/~apw/stress/
Best regards,
--
Javier Martinez Canillas
Open Source Group
Samsung Research America
^ permalink raw reply
* [PATCH 2/2] remoteproc: Remove firmware_loading_complete
From: Bjorn Andersson @ 2016-12-16 19:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <948b5b23-9440-00ef-7cf9-eca62ea93165@st.com>
On Fri 16 Dec 00:26 PST 2016, loic pallardy wrote:
>
>
> On 12/16/2016 01:03 AM, Sarangdhar Joshi wrote:
> >rproc_del() waits on firmware_loading_complete in order to
> >make sure rproc_add() completed successfully before calling
> >rproc_shutdown(). However since rproc_add() will always be
> >called before rproc_del(), we do not need to wait on
> >firmware_loading_complete. Drop this completion variable
> >altogether.
> >
> Hi,
>
> firmware_loading_complete is used to synchronize all operations on rproc
> with parallel work launched by request_firmware_nowait.
We had a deadlock scenario in this code, where a call to rproc_boot()
would grab the rproc mutex and the request_firmware_nowait() callback
would wait on this lock before it would signal the completion that the
rproc_boot() was waiting for.
As the request_firmware_nowait() doesn't do anything other than handle
auto_boot and signal the completion - and there is an internal sleep
mechanism for handling concurrent request_firmware calls - I posted a
patch and dropped the rproc_boot() wait thing.
> rproc_add could be done and firmware loading still pending. In that case
> rproc_del mustn't be called before end of the procedure.
You're right.
We might have an outstanding request_firmware_nowait() when we hit
rproc_del() and we might free the underlaying rproc context.
Holding a reference over the request_firmware_nowait() would solve this,
but would cause issues if we get a rproc_add() from the same driver
(e.g. after module unload/load) before the firmware timer has fired -
and released the resources.
This issue could be remedied by moving the rproc_delete_debug_dir() to
rproc_del() and aim for not having any objects exposed outside the
remoteproc core once rproc_del() returns.
>
> If you decide to remove this synchronization you need either to modify rproc
> boot sequence or to replace it by something else.
>
I agree.
Regards,
Bjorn
^ permalink raw reply
* [PATCH] i2c: mux: pca954x: fix i2c mux selection caching
From: Russell King @ 2016-12-16 20:06 UTC (permalink / raw)
To: linux-arm-kernel
smbus functions return -ve on error, 0 on success. However,
__i2c_transfer() have a different return signature - -ve on error, or
number of buffers transferred (which may be zero or greater.)
The upshot of this is that the sense of the test is reversed when using
the mux on a bus supporting the master_xfer method: we cache the value
and never retry if we fail to transfer any buffers, but if we succeed,
we clear the cached value.
Fix this.
Fixes: 463e8f845cbf ("i2c: mux: pca954x: retry updating the mux selection on failure")
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/i2c/muxes/i2c-mux-pca954x.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index 8bc3d36d2837..b6d62ecbd5b6 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -179,7 +179,7 @@ static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
/* Only select the channel if its different from the last channel */
if (data->last_chan != regval) {
ret = pca954x_reg_write(muxc->parent, client, regval);
- data->last_chan = ret ? 0 : regval;
+ data->last_chan = ret >= 0 ? regval : 0;
}
return ret;
--
2.7.4
^ permalink raw reply related
* [PATCH v7 1/2] provide lock-less versions of clk_{enable|disable}
From: David Lechner @ 2016-12-16 20:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <35adff3d-c6a6-7b65-fb61-6da447dd6c3f@baylibre.com>
On 12/12/2016 04:27 AM, Alexandre Bailon wrote:
> On 12/12/2016 10:02 AM, Sekhar Nori wrote:
>> Hi Uwe,
>>
>> On Saturday 10 December 2016 12:24 AM, Uwe Kleine-K?nig wrote:
>>> Hello,
>>>
>>> On Fri, Dec 09, 2016 at 05:59:32PM +0100, Alexandre Bailon wrote:
>>>> Rename __clk_{enable|disble} in davinci_clk_{enable|disable}.
>>>> davinci_clk_{enable|disable} is a lock-less version of
>>>> clk_{enable|disable}.
>>>> This is useful to recursively enable clock without doing recursive call
>>>> to clk_enable(), which would cause a recursive locking issue.
>>>> The lock-less version could be used by example by the usb20 phy clock,
>>>> that need to enable the usb20 clock before to start.
>>>
>>> I wouldn't call that lock-less. The difference is that the newly exposed
>>> funcion requires the caller to already hold the lock. So maybe a better
>>
>> Right.
> Is it enough to update the commit message or should I also update the
> patch title?
> If so, could you suggest one because I don't know how to describe it
> shortly.
How about... "ARM: davinci: Make __clk_{enable,disable} functions public"
>
> Thanks,
> Alexandre
>
^ permalink raw reply
* [PATCH] i2c: mux: pca954x: fix i2c mux selection caching
From: Peter Rosin @ 2016-12-16 21:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <E1cHylf-00071o-KZ@rmk-PC.armlinux.org.uk>
On 2016-12-16 21:06, Russell King wrote:
> smbus functions return -ve on error, 0 on success. However,
> __i2c_transfer() have a different return signature - -ve on error, or
> number of buffers transferred (which may be zero or greater.)
>
> The upshot of this is that the sense of the test is reversed when using
> the mux on a bus supporting the master_xfer method: we cache the value
> and never retry if we fail to transfer any buffers, but if we succeed,
> we clear the cached value.
Ouch! Thanks for catching this.
> Fix this.
But lets fix the corner case of __i2c_transfer returning 0 instead of
the expected 1 as well (not sure if that's even possible, but lets close
the possibility just in case), so I'd prefer if you could fix
pca954x_reg_write() to return 0 iff __i2c_transfer(...) returns 1
instead, and -EREMOTEIO on other non-negative return values. Thanks!
Cheers,
peda
> Fixes: 463e8f845cbf ("i2c: mux: pca954x: retry updating the mux selection on failure")
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
> ---
> drivers/i2c/muxes/i2c-mux-pca954x.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
> index 8bc3d36d2837..b6d62ecbd5b6 100644
> --- a/drivers/i2c/muxes/i2c-mux-pca954x.c
> +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
> @@ -179,7 +179,7 @@ static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
> /* Only select the channel if its different from the last channel */
> if (data->last_chan != regval) {
> ret = pca954x_reg_write(muxc->parent, client, regval);
> - data->last_chan = ret ? 0 : regval;
> + data->last_chan = ret >= 0 ? regval : 0;
> }
>
> return ret;
>
^ permalink raw reply
* [PATCH] ARM: disallow ARM_THUMB for ARMv4 builds
From: Arnd Bergmann @ 2016-12-16 21:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu93Gtkh7spA-HUHNKSmDjhK_nuwR9KaDu8=S2GDyhUJCA@mail.gmail.com>
On Friday, December 16, 2016 5:20:22 PM CET Ard Biesheuvel wrote:
>
> Can't we use the old
>
> tst lr, #1
> moveq pc, lr
> bx lr
>
> trick? (where bx lr needs to be emitted as a plain opcode to hide it
> from the assembler)
>
Yes, that should work around the specific problem in theory, but back
when Jonas tried it, it still didn't work. There may also be other
problems in that configuration.
Arnd
^ permalink raw reply
* [PATCH v6 0/5] Altera Cyclone Passive Serial SPI FPGA Manager
From: Joshua Clayton @ 2016-12-16 23:17 UTC (permalink / raw)
To: linux-arm-kernel
This series adds an FPGA manager for Altera cyclone FPGAs
that can program them using an spi port and a couple of gpios, using
Alteras passive serial protocol.
Need ACKs from ARCH maintainers for ARCH specific implementations of
__arch_bitrev8x4(), and I've added more ARCHes, so will need more ACKS,
but the generic code will work without that patch, so if there is a holdup
the rest of the series can go in without patch 2
Changes from v5:
- Rebased on next-20161214xi
- Corrected for FPGA Mgr API change in write_init() and write_complete()
- Better describe the device cyclone-ps-spi runs on in the file header.
- Split the bitrev8x4 patch into generic and arch specific patches...
- Added AARCH64 and MIPS implementations of bitrev8x4()... they all have to
have an implementation for it to compile cleanly across platforms
- Added the changes to imx6q-evi.dts to the patch set.
Changes from v4:
- Added the needed return statement to __arch_bitrev8x4()
- Added Rob Herrings ACK for and fix a typo in the commit log of patch 2
Changes from v3:
- Fixed up the state() function to return the state of the status pin
reqested by Alan Tull
- Switched the pin to ACTIVE_LOW and coresponding logic level, and updated
the corresponding documentation. Thanks Rob Herring for pointing out my
mistake.
- Per Rob Herring, switched from "gpio" to "gpios" in dts
Changes from v2:
- Merged patch 3 and 4 as suggested in review by Moritz Fischer
- Changed FPGA_MIN_DELAY from 250 to 50 ms is the time advertized by
Altera. This now works, as we don't assume it is done
Changes from v1:
- Changed the name from cyclone-spi-fpga-mgr to cyclone-ps-spi-fpga-mgr
This name change was requested by Alan Tull, to be specific about which
programming method is being employed on the fpga.
- Changed the name of the reset-gpio to config-gpio to closer match the
way the pins are described in the Altera manual
- Moved MODULE_LICENCE, _AUTHOR, and _DESCRIPTION to the bottom
- Added a bitrev8x4() function to the bitrev headers and implemented ARM
const, runtime, and ARM specific faster versions (This may end up
needing to be a standalone patch)
- Moved the bitswapping into cyclonespi_write(), as requested.
This falls short of my desired generic lsb first spi support, but is a step
in that direction.
- Fixed whitespace problems introduced during refactoring
- Replaced magic number for initial delay with a descriptive macro
- Poll the fpga to see when it is ready rather than a fixed 1 ms sleep
Joshua Clayton (5):
lib: add bitrev8x4()
lib: implement __arch_bitrev8x4()
doc: dt: add cyclone-ps-spi binding document
fpga manager: Add cyclone-ps-spi driver for Altera FPGAs
ARM: dts: imx6q-evi: support cyclone-ps-spi
.../bindings/fpga/cyclone-ps-spi-fpga-mgr.txt | 25 +++
arch/arm/boot/dts/imx6q-evi.dts | 16 ++
arch/arm/include/asm/bitrev.h | 6 +
arch/arm64/include/asm/bitrev.h | 6 +
arch/mips/include/asm/bitrev.h | 6 +
drivers/fpga/Kconfig | 7 +
drivers/fpga/Makefile | 1 +
drivers/fpga/cyclone-ps-spi.c | 186 +++++++++++++++++++++
include/linux/bitrev.h | 26 +++
9 files changed, 279 insertions(+)
create mode 100644 Documentation/devicetree/bindings/fpga/cyclone-ps-spi-fpga-mgr.txt
create mode 100644 drivers/fpga/cyclone-ps-spi.c
--
2.9.3
^ permalink raw reply
* [PATCH v6 1/5] lib: add bitrev8x4()
From: Joshua Clayton @ 2016-12-16 23:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1481918884.git.stillcompiling@gmail.com>
Add a function to reverse bytes within a 32 bit word.
Operate on a u32 rather than individual bytes.
ARCH specific versions require substantially fewer instructions than
working a byte at a time.
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
include/linux/bitrev.h | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/include/linux/bitrev.h b/include/linux/bitrev.h
index fb790b8..868dcb6 100644
--- a/include/linux/bitrev.h
+++ b/include/linux/bitrev.h
@@ -27,6 +27,14 @@ static inline u32 __bitrev32(u32 x)
return (__bitrev16(x & 0xffff) << 16) | __bitrev16(x >> 16);
}
+static inline u32 __bitrev8x4(u32 x)
+{
+ return(__bitrev8(x & 0xff) |
+ (__bitrev8((x >> 8) & 0xff) << 8) |
+ (__bitrev8((x >> 16) & 0xff) << 16) |
+ (__bitrev8((x >> 24) & 0xff) << 24));
+}
+
#endif /* CONFIG_HAVE_ARCH_BITREVERSE */
#define __constant_bitrev32(x) \
@@ -50,6 +58,15 @@ static inline u32 __bitrev32(u32 x)
__x; \
})
+#define __constant_bitrev8x4(x) \
+({ \
+ u32 __x = x; \
+ __x = ((__x & (u32)0xF0F0F0F0UL) >> 4) | ((__x & (u32)0x0F0F0F0FUL) << 4); \
+ __x = ((__x & (u32)0xCCCCCCCCUL) >> 2) | ((__x & (u32)0x33333333UL) << 2); \
+ __x = ((__x & (u32)0xAAAAAAAAUL) >> 1) | ((__x & (u32)0x55555555UL) << 1); \
+ __x; \
+})
+
#define __constant_bitrev8(x) \
({ \
u8 __x = x; \
@@ -75,6 +92,14 @@ static inline u32 __bitrev32(u32 x)
__bitrev16(__x); \
})
+#define bitrev8x4(x) \
+({ \
+ u32 __x = x; \
+ __builtin_constant_p(__x) ? \
+ __constant_bitrev8x4(__x) : \
+ __bitrev8x4(__x); \
+})
+
#define bitrev8(x) \
({ \
u8 __x = x; \
--
2.9.3
^ permalink raw reply related
* [PATCH v6 2/5] lib: implement __arch_bitrev8x4()
From: Joshua Clayton @ 2016-12-16 23:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1481918884.git.stillcompiling@gmail.com>
Implement faster bitrev8x4() for arm, arm64 and mips, all the platforms
with CONFIG_HAVE_ARCH_BITREVERSE.
ARM platforms just need a byteswap added to the existing __arch_bitrev32()
Amusingly, the mips implementation is exactly the opposite, requiring
removal of the byteswap from its __arch_bitrev32()
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
arch/arm/include/asm/bitrev.h | 6 ++++++
arch/arm64/include/asm/bitrev.h | 6 ++++++
arch/mips/include/asm/bitrev.h | 6 ++++++
include/linux/bitrev.h | 1 +
4 files changed, 19 insertions(+)
diff --git a/arch/arm/include/asm/bitrev.h b/arch/arm/include/asm/bitrev.h
index ec291c3..9482f78 100644
--- a/arch/arm/include/asm/bitrev.h
+++ b/arch/arm/include/asm/bitrev.h
@@ -17,4 +17,10 @@ static __always_inline __attribute_const__ u8 __arch_bitrev8(u8 x)
return __arch_bitrev32((u32)x) >> 24;
}
+static __always_inline __attribute_const__ u32 __arch_bitrev8x4(u32 x)
+{
+ __asm__ ("rbit %0, %1; rev %0, %0" : "=r" (x) : "r" (x));
+ return x;
+}
+
#endif
diff --git a/arch/arm64/include/asm/bitrev.h b/arch/arm64/include/asm/bitrev.h
index a5a0c36..1801078 100644
--- a/arch/arm64/include/asm/bitrev.h
+++ b/arch/arm64/include/asm/bitrev.h
@@ -16,4 +16,10 @@ static __always_inline __attribute_const__ u8 __arch_bitrev8(u8 x)
return __arch_bitrev32((u32)x) >> 24;
}
+static __always_inline __attribute_const__ u32 __arch_bitrev8x4(u32 x)
+{
+ __asm__ ("rbit %0, %1; rev %0, %0" : "=r" (x) : "r" (x));
+ return x;
+}
+
#endif
diff --git a/arch/mips/include/asm/bitrev.h b/arch/mips/include/asm/bitrev.h
index bc739a4..9ac6439 100644
--- a/arch/mips/include/asm/bitrev.h
+++ b/arch/mips/include/asm/bitrev.h
@@ -27,4 +27,10 @@ static __always_inline __attribute_const__ u8 __arch_bitrev8(u8 x)
return ret;
}
+static __always_inline __attribute_const__ u32 __arch_bitrev8x4(u32 x)
+{
+ u32 ret;
+ asm("bitswap %0, %1" : "=r"(ret) : "r"(x));
+ return ret;
+}
#endif /* __MIPS_ASM_BITREV_H__ */
diff --git a/include/linux/bitrev.h b/include/linux/bitrev.h
index 868dcb6..b1cfa1a 100644
--- a/include/linux/bitrev.h
+++ b/include/linux/bitrev.h
@@ -9,6 +9,7 @@
#define __bitrev32 __arch_bitrev32
#define __bitrev16 __arch_bitrev16
#define __bitrev8 __arch_bitrev8
+#define __bitrev8x4 __arch_bitrev8x4
#else
extern u8 const byte_rev_table[256];
--
2.9.3
^ permalink raw reply related
* [PATCH v6 3/5] doc: dt: add cyclone-ps-spi binding document
From: Joshua Clayton @ 2016-12-16 23:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1481918884.git.stillcompiling@gmail.com>
Describe a cyclone-ps-spi devicetree entry, required features
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
Acked-by: Rob Herring <robh@kernel.org>
---
.../bindings/fpga/cyclone-ps-spi-fpga-mgr.txt | 25 ++++++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 Documentation/devicetree/bindings/fpga/cyclone-ps-spi-fpga-mgr.txt
diff --git a/Documentation/devicetree/bindings/fpga/cyclone-ps-spi-fpga-mgr.txt b/Documentation/devicetree/bindings/fpga/cyclone-ps-spi-fpga-mgr.txt
new file mode 100644
index 0000000..3f515c7
--- /dev/null
+++ b/Documentation/devicetree/bindings/fpga/cyclone-ps-spi-fpga-mgr.txt
@@ -0,0 +1,25 @@
+Altera Cyclone Passive Serial SPI FPGA Manager
+
+Altera Cyclone FPGAs support a method of loading the bitstream over what is
+referred to as "passive serial".
+The passive serial link is not technically spi, and might require extra
+circuits in order to play nicely with other spi slaves on the same bus.
+
+See https://www.altera.com/literature/hb/cyc/cyc_c51013.pdf
+
+Required properties:
+- compatible : should contain "altr,cyclone-ps-spi-fpga-mgr"
+- reg : spi slave id of the fpga
+- config-gpios : config pin (referred to as nCONFIG in the cyclone manual)
+- status-gpios : status pin (referred to as nSTATUS in the cyclone manual)
+
+both gpios pins are normally active low open drain.
+
+Example:
+ fpga_spi: evi-fpga-spi at 0 {
+ compatible = "altr,cyclone-ps-spi-fpga-mgr";
+ spi-max-frequency = <20000000>;
+ reg = <0>;
+ config-gpios = <&gpio4 9 GPIO_ACTIVE_LOW>;
+ status-gpios = <&gpio4 11 GPIO_ACTIVE_LOW>;
+ };
--
2.9.3
^ permalink raw reply related
* [PATCH v6 4/5] fpga manager: Add cyclone-ps-spi driver for Altera FPGAs
From: Joshua Clayton @ 2016-12-16 23:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1481918884.git.stillcompiling@gmail.com>
cyclone-ps-spi loads FPGA firmware over spi, using the "passive serial"
interface on Altera Cyclone FPGAS.
This is one of the simpler ways to set up an FPGA at runtime.
The signal interface is close to unidirectional spi with lsb first.
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
drivers/fpga/Kconfig | 7 ++
drivers/fpga/Makefile | 1 +
drivers/fpga/cyclone-ps-spi.c | 186 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 194 insertions(+)
create mode 100644 drivers/fpga/cyclone-ps-spi.c
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index ce861a2..e6c032d 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -20,6 +20,13 @@ config FPGA_REGION
FPGA Regions allow loading FPGA images under control of
the Device Tree.
+config FPGA_MGR_CYCLONE_PS_SPI
+ tristate "Altera Cyclone FPGA Passive Serial over SPI"
+ depends on SPI
+ help
+ FPGA manager driver support for Altera Cyclone using the
+ passive serial interface over SPI
+
config FPGA_MGR_SOCFPGA
tristate "Altera SOCFPGA FPGA Manager"
depends on ARCH_SOCFPGA || COMPILE_TEST
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 8df07bc..a112bef 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_FPGA) += fpga-mgr.o
# FPGA Manager Drivers
+obj-$(CONFIG_FPGA_MGR_CYCLONE_PS_SPI) += cyclone-ps-spi.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
diff --git a/drivers/fpga/cyclone-ps-spi.c b/drivers/fpga/cyclone-ps-spi.c
new file mode 100644
index 0000000..f9126f9
--- /dev/null
+++ b/drivers/fpga/cyclone-ps-spi.c
@@ -0,0 +1,186 @@
+/**
+ * Altera Cyclone Passive Serial SPI Driver
+ *
+ * Copyright (c) 2017 United Western Technologies, Corporation
+ *
+ * Joshua Clayton <stillcompiling@gmail.com>
+ *
+ * Manage Altera FPGA firmware that is loaded over spi using the passive
+ * serial configuration method.
+ * Firmware must be in binary "rbf" format.
+ * Works on Cyclone V. Should work on cyclone series.
+ * May work on other Altera FPGAs.
+ *
+ */
+
+#include <linux/bitrev.h>
+#include <linux/delay.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/sizes.h>
+
+#define FPGA_RESET_TIME 50 /* time in usecs to trigger FPGA config */
+#define FPGA_MIN_DELAY 50 /* min usecs to wait for config status */
+#define FPGA_MAX_DELAY 1000 /* max usecs to wait for config status */
+
+struct cyclonespi_conf {
+ struct gpio_desc *config;
+ struct gpio_desc *status;
+ struct spi_device *spi;
+};
+
+static const struct of_device_id of_ef_match[] = {
+ { .compatible = "altr,cyclone-ps-spi-fpga-mgr", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_ef_match);
+
+static enum fpga_mgr_states cyclonespi_state(struct fpga_manager *mgr)
+{
+ struct cyclonespi_conf *conf = (struct cyclonespi_conf *)mgr->priv;
+
+ if (gpiod_get_value(conf->status))
+ return FPGA_MGR_STATE_RESET;
+
+ return FPGA_MGR_STATE_UNKNOWN;
+}
+
+static int cyclonespi_write_init(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ const char *buf, size_t count)
+{
+ struct cyclonespi_conf *conf = (struct cyclonespi_conf *)mgr->priv;
+ int i;
+
+ if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
+ dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
+ return -EINVAL;
+ }
+
+ gpiod_set_value(conf->config, 1);
+ usleep_range(FPGA_RESET_TIME, FPGA_RESET_TIME + 20);
+ if (!gpiod_get_value(conf->status)) {
+ dev_err(&mgr->dev, "Status pin should be low.\n");
+ return -EIO;
+ }
+
+ gpiod_set_value(conf->config, 0);
+ for (i = 0; i < (FPGA_MAX_DELAY / FPGA_MIN_DELAY); i++) {
+ usleep_range(FPGA_MIN_DELAY, FPGA_MIN_DELAY + 20);
+ if (!gpiod_get_value(conf->status))
+ return 0;
+ }
+
+ dev_err(&mgr->dev, "Status pin not ready.\n");
+ return -EIO;
+}
+
+static void rev_buf(void *buf, size_t len)
+{
+ u32 *fw32 = (u32 *)buf;
+ const u32 *fw_end = (u32 *)(buf + len);
+
+ /* set buffer to lsb first */
+ while (fw32 < fw_end) {
+ *fw32 = bitrev8x4(*fw32);
+ fw32++;
+ }
+}
+
+static int cyclonespi_write(struct fpga_manager *mgr, const char *buf,
+ size_t count)
+{
+ struct cyclonespi_conf *conf = (struct cyclonespi_conf *)mgr->priv;
+ const char *fw_data = buf;
+ const char *fw_data_end = fw_data + count;
+
+ while (fw_data < fw_data_end) {
+ int ret;
+ size_t stride = min(fw_data_end - fw_data, SZ_4K);
+
+ rev_buf((void *)fw_data, stride);
+ ret = spi_write(conf->spi, fw_data, stride);
+ if (ret) {
+ dev_err(&mgr->dev, "spi error in firmware write: %d\n",
+ ret);
+ return ret;
+ }
+ fw_data += stride;
+ }
+
+ return 0;
+}
+
+static int cyclonespi_write_complete(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
+{
+ struct cyclonespi_conf *conf = (struct cyclonespi_conf *)mgr->priv;
+
+ if (gpiod_get_value(conf->status)) {
+ dev_err(&mgr->dev, "Error during configuration.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const struct fpga_manager_ops cyclonespi_ops = {
+ .state = cyclonespi_state,
+ .write_init = cyclonespi_write_init,
+ .write = cyclonespi_write,
+ .write_complete = cyclonespi_write_complete,
+};
+
+static int cyclonespi_probe(struct spi_device *spi)
+{
+ struct cyclonespi_conf *conf = devm_kzalloc(&spi->dev, sizeof(*conf),
+ GFP_KERNEL);
+
+ if (!conf)
+ return -ENOMEM;
+
+ conf->spi = spi;
+ conf->config = devm_gpiod_get(&spi->dev, "config", GPIOD_OUT_HIGH);
+ if (IS_ERR(conf->config)) {
+ dev_err(&spi->dev, "Failed to get config gpio: %ld\n",
+ PTR_ERR(conf->config));
+ return PTR_ERR(conf->config);
+ }
+
+ conf->status = devm_gpiod_get(&spi->dev, "status", GPIOD_IN);
+ if (IS_ERR(conf->status)) {
+ dev_err(&spi->dev, "Failed to get status gpio: %ld\n",
+ PTR_ERR(conf->status));
+ return PTR_ERR(conf->status);
+ }
+
+ return fpga_mgr_register(&spi->dev,
+ "Altera Cyclone PS SPI FPGA Manager",
+ &cyclonespi_ops, conf);
+}
+
+static int cyclonespi_remove(struct spi_device *spi)
+{
+ fpga_mgr_unregister(&spi->dev);
+
+ return 0;
+}
+
+static struct spi_driver cyclonespi_driver = {
+ .driver = {
+ .name = "cyclone-ps-spi",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_ef_match),
+ },
+ .probe = cyclonespi_probe,
+ .remove = cyclonespi_remove,
+};
+
+module_spi_driver(cyclonespi_driver)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joshua Clayton <stillcompiling@gmail.com>");
+MODULE_DESCRIPTION("Module to load Altera FPGA firmware over spi");
--
2.9.3
^ permalink raw reply related
* [PATCH v6 5/5] ARM: dts: imx6q-evi: support cyclone-ps-spi
From: Joshua Clayton @ 2016-12-16 23:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1481918884.git.stillcompiling@gmail.com>
Add support for Altera cyclone V FPGA connected to an spi port
to the evi devicetree file
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
arch/arm/boot/dts/imx6q-evi.dts | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm/boot/dts/imx6q-evi.dts b/arch/arm/boot/dts/imx6q-evi.dts
index 7c7c1a8..ec4d365 100644
--- a/arch/arm/boot/dts/imx6q-evi.dts
+++ b/arch/arm/boot/dts/imx6q-evi.dts
@@ -95,6 +95,15 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi1 &pinctrl_ecspi1cs>;
status = "okay";
+
+ fpga_spi: cyclonespi at 0 {
+ compatible = "altr,cyclone-ps-spi-fpga-mgr";
+ spi-max-frequency = <20000000>;
+ reg = <0>;
+ pinctrl-0 = <&pinctrl_fpgaspi>;
+ config-gpios = <&gpio4 9 GPIO_ACTIVE_LOW>;
+ status-gpios = <&gpio4 11 GPIO_ACTIVE_LOW>;
+ };
};
&ecspi3 {
@@ -322,6 +331,13 @@
>;
};
+ pinctrl_fpgaspi: fpgaspigrp {
+ fsl,pins = <
+ MX6QDL_PAD_KEY_ROW1__GPIO4_IO09 0x1b0b0
+ MX6QDL_PAD_KEY_ROW2__GPIO4_IO11 0x1b0b0
+ >;
+ };
+
pinctrl_gpminand: gpminandgrp {
fsl,pins = <
MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1
--
2.9.3
^ permalink raw reply related
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