* [Linaro-acpi] [PATCH V1 1/2] PCI: thunder: Enable ACPI PCI controller for ThunderX pass2.x silicon version
From: Robert Richter @ 2016-12-08 16:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161202162743.GB9903@bhelgaas-glaptop.roam.corp.google.com>
On 02.12.16 10:27:43, Bjorn Helgaas wrote:
> On Fri, Dec 02, 2016 at 11:45:00AM +0100, Robert Richter wrote:
> > On 02.12.16 11:06:24, Tomasz Nowicki wrote:
> > > On 02.12.2016 07:42, Duc Dang wrote:
> >
> > > >@@ -98,16 +98,16 @@ struct mcfg_fixup {
> > > > { "CAVIUM", "THUNDERX", rev, seg, MCFG_BUS_ANY, \
> > > > &pci_thunder_ecam_ops }
> > > > /* SoC pass1.x */
> > > >- THUNDER_PEM_QUIRK(2, 0), /* off-chip devices */
> > > >- THUNDER_PEM_QUIRK(2, 1), /* off-chip devices */
> > > >- THUNDER_ECAM_QUIRK(2, 0),
> > > >- THUNDER_ECAM_QUIRK(2, 1),
> > > >- THUNDER_ECAM_QUIRK(2, 2),
> > > >- THUNDER_ECAM_QUIRK(2, 3),
> > > >- THUNDER_ECAM_QUIRK(2, 10),
> > > >- THUNDER_ECAM_QUIRK(2, 11),
> > > >- THUNDER_ECAM_QUIRK(2, 12),
> > > >- THUNDER_ECAM_QUIRK(2, 13),
> > > >+ THUNDER_PEM_QUIRK(2, 0UL), /* off-chip devices */
> > > >+ THUNDER_PEM_QUIRK(2, 1UL), /* off-chip devices */
> > > >+ THUNDER_ECAM_QUIRK(2, 0UL),
> > > >+ THUNDER_ECAM_QUIRK(2, 1UL),
> > > >+ THUNDER_ECAM_QUIRK(2, 2UL),
> > > >+ THUNDER_ECAM_QUIRK(2, 3UL),
> > > >+ THUNDER_ECAM_QUIRK(2, 10UL),
> > > >+ THUNDER_ECAM_QUIRK(2, 11UL),
> > > >+ THUNDER_ECAM_QUIRK(2, 12UL),
> > > >+ THUNDER_ECAM_QUIRK(2, 13UL),
> > > >
> > >
> > > The UL suffix is needed for *THUNDER_PEM_QUIRK* only. THUNDER_ECAM_QUIRK is
> > > fine.
> >
> > We should better make the type cast part of the macro.
> >
> > + this:
> >
> > ---
> > #define THUNDER_MCFG_RES(addr, node) \
> > DEFINE_RES_MEM(addr + (node << 44), 0x39 * SZ_16M)
> > ---
> >
> > The args in the macro need parentheses.
>
> Would you mind sending me a little incremental patch doing what you
> want? I could try myself, but since I don't have an arm64 cross-build
> setup, I'm working in the dark.
Your current branch looks good.
5d06f9125ec0 PCI: Explain ARM64 ACPI/MCFG quirk Kconfig and build strategy
Thanks,
-Robert
^ permalink raw reply
* Tearing down DMA transfer setup after DMA client has finished
From: Måns Rullgård @ 2016-12-08 16:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208155043.GE6408@localhost>
Vinod Koul <vinod.koul@intel.com> writes:
> On Thu, Dec 08, 2016 at 12:20:30PM +0000, M?ns Rullg?rd wrote:
>> >
>> > I'm far from claiming that drivers/tty/serial/sh-sci.c is perfect, but
>> > it does request DMA channels at open time, not at probe time.
>>
>> In the part quoted above, I said most drivers request dma channels in
>> their probe or open functions. For the purposes of this discussion,
>> that distinction is irrelevant. In either case, the channel is held
>> indefinitely.
>
> And the answer was it is wrong and not _all_ do that!!
Show me one that doesn't.
>> If this wasn't the correct way to use the dmaengine,
>> there would be no need for the virt-dma helpers which are specifically
>> designed for cases the one currently at hand.
>
> That is incorrect.
>
> virt-dma helps to have multiple request from various clients. For many
> controllers which implement a SW mux, they can transfer data for one client
> and then next transfer can be for some other one.
>
> This allows better utilization of dma channels and helps in case where we
> have fewer dma channels than users.
Which is *exactly* the situation we have here.
I have no idea what you're arguing for or against any more. Perhaps
you're just arguing.
>> The only problem we have is that nobody envisioned hardware where the
>> dma engine indicates completion slightly too soon. I suspect there's a
>> fifo or such somewhere, and the interrupt is triggered when the last
>> byte has been placed in the fifo rather than when it has been removed
>> which would have been more correct.
>
> That is pretty common hardware optimization but usually hardware shows up
> with flush commands to let in flight transactions be completed.
Well, this hardware seems to lack that particular feature. Pretending
otherwise isn't helping.
--
M?ns Rullg?rd
^ permalink raw reply
* Tearing down DMA transfer setup after DMA client has finished
From: Vinod Koul @ 2016-12-08 16:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <91b0d10c-1bc2-c3e1-4088-f4ad9adcd6c0@free.fr>
On Thu, Dec 08, 2016 at 11:54:51AM +0100, Mason wrote:
> On 08/12/2016 11:39, Vinod Koul wrote:
>
> > On Wed, Dec 07, 2016 at 04:45:58PM +0000, M?ns Rullg?rd wrote:
> >
> >> Vinod Koul <vinod.koul@intel.com> writes:
> >>
> >>> On Tue, Dec 06, 2016 at 01:14:20PM +0000, M?ns Rullg?rd wrote:
> >>>>
> >>>> That's not going to work very well. Device drivers typically request
> >>>> dma channels in their probe functions or when the device is opened.
> >>>> This means that reserving one of the few channels there will inevitably
> >>>> make some other device fail to operate.
> >>>
> >>> No that doesn't make sense at all, you should get a channel only when you
> >>> want to use it and not in probe!
> >>
> >> Tell that to just about every single driver ever written.
> >
> > Not really, few do yes which is wrong but not _all_ do that.
>
> Vinod,
>
> Could you explain something to me in layman's terms?
>
> I have a NAND Flash Controller driver that depends on the
> DMA driver under discussion.
>
> Suppose I move the dma_request_chan() call from the driver's
> probe function, to the actual DMA transfer function.
>
> I would want dma_request_chan() to put the calling thread
> to sleep until a channel becomes available (possibly with
> a timeout value).
>
> But Maxime told me dma_request_chan() will just return
> -EBUSY if no channels are available.
That is correct
> Am I supposed to busy wait in my driver's DMA function
> until a channel becomes available?
If someone else is using the channels then the bust wait will not help, so
in this case you should fall back to PIO, few drivers do that
> I don't understand how the multiplexing of few memory
> channels to many clients is supposed to happen efficiently?
To make it efficient, disregarding your Sbox HW issue, the solution is
virtual channels. You can delink physical channels and virtual channels. If
one has SW controlled MUX then a channel can service any client. For few
controllers request lines are hard wired so they cant use any channel. But
if you dont have this restriction then driver can queue up many transactions
from different controllers.
--
~Vinod
^ permalink raw reply
* [PATCH] MAINTAINERS: Add Patchwork URL to Samsung Exynos entry
From: Krzysztof Kozlowski @ 2016-12-08 16:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <F4BA1C22-7E6C-4791-9B0D-37B3F647E0EB@gmail.com>
On Thu, Dec 08, 2016 at 11:24:43AM +0900, Kukjin Kim wrote:
> 2016. 12. 8. 02:18 Krzysztof Kozlowski <krzk@kernel.org> wrote:
>
> > I use Patchwork for handling incoming patches. Put its address here so
> > submitters could know what is in the queue.
>
> > Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
> > ---
> > MAINTAINERS | 1 +
> > 1 file changed, 1 insertion(+)
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 191887bdc49b..ec5137c39572 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -1689,6 +1689,7 @@ M: Krzysztof Kozlowski <krzk@kernel.org>
> > R: Javier Martinez Canillas <javier@osg.samsung.com>
> > L: linux-arm-kernel at lists.infradead.org (moderated for non-subscribers)
> > L: linux-samsung-soc at vger.kernel.org (moderated for non-subscribers)
> > +Q: https://patchwork.kernel.org/project/linux-samsung-soc/list/
>
> to use http would be better instead of https?
I think HTTPS is the preferred way. HTTP is insecure, easy to eavesdrop
so whenever it is possible we should choose HTTPS.
>
> then,
>
> Acked-by: Kukjin Kim <kgene@kernel.org>
Thanks!
Best regards,
Krzysztof
^ permalink raw reply
* Tearing down DMA transfer setup after DMA client has finished
From: Måns Rullgård @ 2016-12-08 16:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208154018.GD6408@localhost>
Vinod Koul <vinod.koul@intel.com> writes:
> On Thu, Dec 08, 2016 at 11:44:53AM +0000, M?ns Rullg?rd wrote:
>> Vinod Koul <vinod.koul@intel.com> writes:
>>
>> > On Wed, Dec 07, 2016 at 04:45:58PM +0000, M?ns Rullg?rd wrote:
>> >> Vinod Koul <vinod.koul@intel.com> writes:
>> >>
>> >> > On Tue, Dec 06, 2016 at 01:14:20PM +0000, M?ns Rullg?rd wrote:
>> >> >>
>> >> >> That's not going to work very well. Device drivers typically request
>> >> >> dma channels in their probe functions or when the device is opened.
>> >> >> This means that reserving one of the few channels there will inevitably
>> >> >> make some other device fail to operate.
>> >> >
>> >> > No that doesnt make sense at all, you should get a channel only when you
>> >> > want to use it and not in probe!
>> >>
>> >> Tell that to just about every single driver ever written.
>> >
>> > Not really, few do yes which is wrong but not _all_ do that.
>>
>> Every driver I ever looked at does. Name one you consider "correct."
>
> That only tells me you haven't looked enough and want to rant!
>
> FWIW look at ALSA-dmaengine lib, thereby every audio driver that uses it. I
> could find other examples and go on and on, but that's besides the point and
> looks like you don't want to listen to people telling you something..
ALSA is a particularly poor example. ALSA drivers request a dma channel
at some point between the device being opened and playback (or capture)
starting. They then set up a cyclic transfer that runs continuously
until playback is stopped. It's a completely different model of
operation than we're talking about here.
--
M?ns Rullg?rd
^ permalink raw reply
* Tearing down DMA transfer setup after DMA client has finished
From: Måns Rullgård @ 2016-12-08 16:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208163755.GH6408@localhost>
Vinod Koul <vinod.koul@intel.com> writes:
> To make it efficient, disregarding your Sbox HW issue, the solution is
> virtual channels. You can delink physical channels and virtual channels. If
> one has SW controlled MUX then a channel can service any client. For few
> controllers request lines are hard wired so they cant use any channel. But
> if you dont have this restriction then driver can queue up many transactions
> from different controllers.
Have you been paying attention at all? This exactly what the driver
ALREADY DOES.
--
M?ns Rullg?rd
^ permalink raw reply
* [PATCH v3] arm64: fpsimd: improve stacking logic in non-interruptible context
From: Ard Biesheuvel @ 2016-12-08 16:49 UTC (permalink / raw)
To: linux-arm-kernel
Currently, we allow kernel mode NEON in softirq or hardirq context by
stacking and unstacking a slice of the NEON register file for each call
to kernel_neon_begin() and kernel_neon_end(), respectively.
Given that
a) a CPU typically spends most of its time in userland, during which time
no kernel mode NEON in process context is in progress,
b) a CPU spends most of its time in the kernel doing other things than
kernel mode NEON when it gets interrupted to perform kernel mode NEON
in softirq context
the stacking and subsequent unstacking is only necessary if we are
interrupting a thread while it is performing kernel mode NEON in process
context, which means that in all other cases, we can simply preserve the
userland FPSIMD state once, and only restore it upon return to userland,
even if we are being invoked from softirq or hardirq context.
So instead of checking whether we are running in interrupt context, keep
track of the level of nested kernel mode NEON calls in progress, and only
perform the eager stack/unstack if the level exceeds 1.
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
v3:
- avoid corruption by concurrent invocations of kernel_neon_begin()/_end()
v2:
- BUG() on unexpected values of the nesting level
- relax the BUG() on num_regs>32 to a WARN, given that nothing actually
breaks in that case
arch/arm64/kernel/fpsimd.c | 47 ++++++++++++++------
1 file changed, 33 insertions(+), 14 deletions(-)
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 394c61db5566..536ddab9316d 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -220,20 +220,35 @@ void fpsimd_flush_task_state(struct task_struct *t)
#ifdef CONFIG_KERNEL_MODE_NEON
-static DEFINE_PER_CPU(struct fpsimd_partial_state, hardirq_fpsimdstate);
-static DEFINE_PER_CPU(struct fpsimd_partial_state, softirq_fpsimdstate);
+/*
+ * Although unlikely, it is possible for three kernel mode NEON contexts to
+ * be live at the same time: process context, softirq context and hardirq
+ * context. So while the userland context is stashed in the thread's fpsimd
+ * state structure, we need two additional levels of storage.
+ */
+static DEFINE_PER_CPU(struct fpsimd_partial_state, nested_fpsimdstate[2]);
+static DEFINE_PER_CPU(atomic_t, kernel_neon_nesting_level);
/*
* Kernel-side NEON support functions
*/
void kernel_neon_begin_partial(u32 num_regs)
{
- if (in_interrupt()) {
- struct fpsimd_partial_state *s = this_cpu_ptr(
- in_irq() ? &hardirq_fpsimdstate : &softirq_fpsimdstate);
+ struct fpsimd_partial_state *s;
+ int level;
+
+ preempt_disable();
+
+ level = atomic_inc_return(this_cpu_ptr(&kernel_neon_nesting_level));
+ BUG_ON(level > 3);
+
+ if (level > 1) {
+ s = this_cpu_ptr(nested_fpsimdstate);
- BUG_ON(num_regs > 32);
- fpsimd_save_partial_state(s, roundup(num_regs, 2));
+ WARN_ON_ONCE(num_regs > 32);
+ num_regs = min(roundup(num_regs, 2), 32U);
+
+ fpsimd_save_partial_state(&s[level - 2], num_regs);
} else {
/*
* Save the userland FPSIMD state if we have one and if we
@@ -241,7 +256,6 @@ void kernel_neon_begin_partial(u32 num_regs)
* that there is no longer userland FPSIMD state in the
* registers.
*/
- preempt_disable();
if (current->mm &&
!test_and_set_thread_flag(TIF_FOREIGN_FPSTATE))
fpsimd_save_state(¤t->thread.fpsimd_state);
@@ -252,13 +266,18 @@ EXPORT_SYMBOL(kernel_neon_begin_partial);
void kernel_neon_end(void)
{
- if (in_interrupt()) {
- struct fpsimd_partial_state *s = this_cpu_ptr(
- in_irq() ? &hardirq_fpsimdstate : &softirq_fpsimdstate);
- fpsimd_load_partial_state(s);
- } else {
- preempt_enable();
+ struct fpsimd_partial_state *s;
+ int level;
+
+ level = atomic_read(this_cpu_ptr(&kernel_neon_nesting_level));
+ BUG_ON(level < 1);
+
+ if (level > 1) {
+ s = this_cpu_ptr(nested_fpsimdstate);
+ fpsimd_load_partial_state(&s[level - 2]);
}
+ atomic_dec(this_cpu_ptr(&kernel_neon_nesting_level));
+ preempt_enable();
}
EXPORT_SYMBOL(kernel_neon_end);
--
2.7.4
^ permalink raw reply related
* Applied "ASoC: zte: spdif: correct ZX_SPDIF_CLK_RAT define" to the asoc tree
From: Mark Brown @ 2016-12-08 16:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481186655-8213-2-git-send-email-shawnguo@kernel.org>
The patch
ASoC: zte: spdif: correct ZX_SPDIF_CLK_RAT define
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From 44b1c9a6e7a2692e761e35ada2ffe84b20c2a377 Mon Sep 17 00:00:00 2001
From: Shawn Guo <shawn.guo@linaro.org>
Date: Thu, 8 Dec 2016 16:44:15 +0800
Subject: [PATCH] ASoC: zte: spdif: correct ZX_SPDIF_CLK_RAT define
The macro ZX_SPDIF_CLK_RAT should be 2 instead of 4. With this
fix, we can get correct audio output on HDMI through SPDIF interface.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Acked-by: Jun Nie <jun.nie@linaro.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
sound/soc/zte/zx-spdif.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/zte/zx-spdif.c b/sound/soc/zte/zx-spdif.c
index 26265ce4caca..9fa6463ce5d7 100644
--- a/sound/soc/zte/zx-spdif.c
+++ b/sound/soc/zte/zx-spdif.c
@@ -71,7 +71,7 @@
#define ZX_VALID_RIGHT_TRACK (2 << 0)
#define ZX_VALID_TRACK_MASK (3 << 0)
-#define ZX_SPDIF_CLK_RAT (4 * 32)
+#define ZX_SPDIF_CLK_RAT (2 * 32)
struct zx_spdif_info {
struct snd_dmaengine_dai_dma_data dma_data;
--
2.10.2
^ permalink raw reply related
* Applied "ASoC: zte: spdif and i2s drivers are not zx296702 specific" to the asoc tree
From: Mark Brown @ 2016-12-08 16:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481186655-8213-1-git-send-email-shawnguo@kernel.org>
The patch
ASoC: zte: spdif and i2s drivers are not zx296702 specific
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From de7975c2a42de889e2b3fd2f7d46f899ad8ccd45 Mon Sep 17 00:00:00 2001
From: Shawn Guo <shawn.guo@linaro.org>
Date: Thu, 8 Dec 2016 16:44:14 +0800
Subject: [PATCH] ASoC: zte: spdif and i2s drivers are not zx296702 specific
ZTE ZX SPDIF and I2S drivers can work on not only ZX296702 but also
other ZTE ZX family SoCs like ZX296718, which is an arm64 platform.
Let's make a few renaming and tweak the Kconfig a bit to get the drivers
available for other ZTE ZX platforms.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Jun Nie <jun.nie@linaro.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
sound/soc/zte/Kconfig | 16 ++++++++--------
sound/soc/zte/Makefile | 4 ++--
sound/soc/zte/{zx296702-i2s.c => zx-i2s.c} | 0
sound/soc/zte/{zx296702-spdif.c => zx-spdif.c} | 0
4 files changed, 10 insertions(+), 10 deletions(-)
rename sound/soc/zte/{zx296702-i2s.c => zx-i2s.c} (100%)
rename sound/soc/zte/{zx296702-spdif.c => zx-spdif.c} (100%)
diff --git a/sound/soc/zte/Kconfig b/sound/soc/zte/Kconfig
index c47eb25e441f..6d8a90d36315 100644
--- a/sound/soc/zte/Kconfig
+++ b/sound/soc/zte/Kconfig
@@ -1,17 +1,17 @@
-config ZX296702_SPDIF
- tristate "ZX296702 spdif"
- depends on SOC_ZX296702 || COMPILE_TEST
+config ZX_SPDIF
+ tristate "ZTE ZX SPDIF Driver Support"
+ depends on ARCH_ZX || COMPILE_TEST
depends on COMMON_CLK
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for codecs attached to the
- zx296702 spdif interface
+ ZTE ZX SPDIF interface
-config ZX296702_I2S
- tristate "ZX296702 i2s"
- depends on SOC_ZX296702 || COMPILE_TEST
+config ZX_I2S
+ tristate "ZTE ZX I2S Driver Support"
+ depends on ARCH_ZX || COMPILE_TEST
depends on COMMON_CLK
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for codecs attached to the
- zx296702 i2s interface
+ ZTE ZX I2S interface
diff --git a/sound/soc/zte/Makefile b/sound/soc/zte/Makefile
index 254ed2c8c1a0..77768f5fd10c 100644
--- a/sound/soc/zte/Makefile
+++ b/sound/soc/zte/Makefile
@@ -1,2 +1,2 @@
-obj-$(CONFIG_ZX296702_SPDIF) += zx296702-spdif.o
-obj-$(CONFIG_ZX296702_I2S) += zx296702-i2s.o
+obj-$(CONFIG_ZX_SPDIF) += zx-spdif.o
+obj-$(CONFIG_ZX_I2S) += zx-i2s.o
diff --git a/sound/soc/zte/zx296702-i2s.c b/sound/soc/zte/zx-i2s.c
similarity index 100%
rename from sound/soc/zte/zx296702-i2s.c
rename to sound/soc/zte/zx-i2s.c
diff --git a/sound/soc/zte/zx296702-spdif.c b/sound/soc/zte/zx-spdif.c
similarity index 100%
rename from sound/soc/zte/zx296702-spdif.c
rename to sound/soc/zte/zx-spdif.c
--
2.10.2
^ permalink raw reply related
* Applied "spi: Add support for Armada 3700 SPI Controller" to the spi tree
From: Mark Brown @ 2016-12-08 16:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208145847.7794-2-romain.perier@free-electrons.com>
The patch
spi: Add support for Armada 3700 SPI Controller
has been applied to the spi tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From 5762ab71eb24a8393a167361510d7ca5e18c99f9 Mon Sep 17 00:00:00 2001
From: Romain Perier <romain.perier@free-electrons.com>
Date: Thu, 8 Dec 2016 15:58:44 +0100
Subject: [PATCH] spi: Add support for Armada 3700 SPI Controller
Marvell Armada 3700 SoC comprises an SPI Controller. This Controller
supports up to 4 SPI slave devices, with dedicated chip selects,supports
SPI mode 0/1/2 and 3, CPIO or Fifo mode with DMA transfers and different
SPI transfer mode (Single, Dual or Quad).
This commit adds basic driver support for FIFO mode. In this mode,
dedicated registers are used to store the instruction, the address, the
read mode and the data. Write and Read FIFO are used to store the
outcoming or incoming data. The data FIFOs are accessible via DMA or by
the CPU. Only the CPU is supported for now.
Signed-off-by: Romain Perier <romain.perier@free-electrons.com>
Tested-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
drivers/spi/Kconfig | 7 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-armada-3700.c | 923 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 931 insertions(+)
create mode 100644 drivers/spi/spi-armada-3700.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b7995474148c..1faad2ce4b4b 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -67,6 +67,13 @@ config SPI_ATH79
This enables support for the SPI controller present on the
Atheros AR71XX/AR724X/AR913X SoCs.
+config SPI_ARMADA_3700
+ tristate "Marvell Armada 3700 SPI Controller"
+ depends on (ARCH_MVEBU && OF) || COMPILE_TEST
+ help
+ This enables support for the SPI controller present on the
+ Marvell Armada 3700 SoCs.
+
config SPI_ATMEL
tristate "Atmel SPI Controller"
depends on HAS_DMA
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index aa939d955521..140ca45aa9d2 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
# SPI master controller drivers (bus)
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
+obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c
new file mode 100644
index 000000000000..e89da0af45d2
--- /dev/null
+++ b/drivers/spi/spi-armada-3700.c
@@ -0,0 +1,923 @@
+/*
+ * Marvell Armada-3700 SPI controller driver
+ *
+ * Copyright (C) 2016 Marvell Ltd.
+ *
+ * Author: Wilson Ding <dingwei@marvell.com>
+ * Author: Romain Perier <romain.perier@free-electrons.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/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/spi/spi.h>
+
+#define DRIVER_NAME "armada_3700_spi"
+
+#define A3700_SPI_TIMEOUT 10
+
+/* SPI Register Offest */
+#define A3700_SPI_IF_CTRL_REG 0x00
+#define A3700_SPI_IF_CFG_REG 0x04
+#define A3700_SPI_DATA_OUT_REG 0x08
+#define A3700_SPI_DATA_IN_REG 0x0C
+#define A3700_SPI_IF_INST_REG 0x10
+#define A3700_SPI_IF_ADDR_REG 0x14
+#define A3700_SPI_IF_RMODE_REG 0x18
+#define A3700_SPI_IF_HDR_CNT_REG 0x1C
+#define A3700_SPI_IF_DIN_CNT_REG 0x20
+#define A3700_SPI_IF_TIME_REG 0x24
+#define A3700_SPI_INT_STAT_REG 0x28
+#define A3700_SPI_INT_MASK_REG 0x2C
+
+/* A3700_SPI_IF_CTRL_REG */
+#define A3700_SPI_EN BIT(16)
+#define A3700_SPI_ADDR_NOT_CONFIG BIT(12)
+#define A3700_SPI_WFIFO_OVERFLOW BIT(11)
+#define A3700_SPI_WFIFO_UNDERFLOW BIT(10)
+#define A3700_SPI_RFIFO_OVERFLOW BIT(9)
+#define A3700_SPI_RFIFO_UNDERFLOW BIT(8)
+#define A3700_SPI_WFIFO_FULL BIT(7)
+#define A3700_SPI_WFIFO_EMPTY BIT(6)
+#define A3700_SPI_RFIFO_FULL BIT(5)
+#define A3700_SPI_RFIFO_EMPTY BIT(4)
+#define A3700_SPI_WFIFO_RDY BIT(3)
+#define A3700_SPI_RFIFO_RDY BIT(2)
+#define A3700_SPI_XFER_RDY BIT(1)
+#define A3700_SPI_XFER_DONE BIT(0)
+
+/* A3700_SPI_IF_CFG_REG */
+#define A3700_SPI_WFIFO_THRS BIT(28)
+#define A3700_SPI_RFIFO_THRS BIT(24)
+#define A3700_SPI_AUTO_CS BIT(20)
+#define A3700_SPI_DMA_RD_EN BIT(18)
+#define A3700_SPI_FIFO_MODE BIT(17)
+#define A3700_SPI_SRST BIT(16)
+#define A3700_SPI_XFER_START BIT(15)
+#define A3700_SPI_XFER_STOP BIT(14)
+#define A3700_SPI_INST_PIN BIT(13)
+#define A3700_SPI_ADDR_PIN BIT(12)
+#define A3700_SPI_DATA_PIN1 BIT(11)
+#define A3700_SPI_DATA_PIN0 BIT(10)
+#define A3700_SPI_FIFO_FLUSH BIT(9)
+#define A3700_SPI_RW_EN BIT(8)
+#define A3700_SPI_CLK_POL BIT(7)
+#define A3700_SPI_CLK_PHA BIT(6)
+#define A3700_SPI_BYTE_LEN BIT(5)
+#define A3700_SPI_CLK_PRESCALE BIT(0)
+#define A3700_SPI_CLK_PRESCALE_MASK (0x1f)
+
+#define A3700_SPI_WFIFO_THRS_BIT 28
+#define A3700_SPI_RFIFO_THRS_BIT 24
+#define A3700_SPI_FIFO_THRS_MASK 0x7
+
+#define A3700_SPI_DATA_PIN_MASK 0x3
+
+/* A3700_SPI_IF_HDR_CNT_REG */
+#define A3700_SPI_DUMMY_CNT_BIT 12
+#define A3700_SPI_DUMMY_CNT_MASK 0x7
+#define A3700_SPI_RMODE_CNT_BIT 8
+#define A3700_SPI_RMODE_CNT_MASK 0x3
+#define A3700_SPI_ADDR_CNT_BIT 4
+#define A3700_SPI_ADDR_CNT_MASK 0x7
+#define A3700_SPI_INSTR_CNT_BIT 0
+#define A3700_SPI_INSTR_CNT_MASK 0x3
+
+/* A3700_SPI_IF_TIME_REG */
+#define A3700_SPI_CLK_CAPT_EDGE BIT(7)
+
+/* Flags and macros for struct a3700_spi */
+#define A3700_INSTR_CNT 1
+#define A3700_ADDR_CNT 3
+#define A3700_DUMMY_CNT 1
+
+struct a3700_spi {
+ struct spi_master *master;
+ void __iomem *base;
+ struct clk *clk;
+ unsigned int irq;
+ unsigned int flags;
+ bool xmit_data;
+ const u8 *tx_buf;
+ u8 *rx_buf;
+ size_t buf_len;
+ u8 byte_len;
+ u32 wait_mask;
+ struct completion done;
+ u32 addr_cnt;
+ u32 instr_cnt;
+ size_t hdr_cnt;
+};
+
+static u32 spireg_read(struct a3700_spi *a3700_spi, u32 offset)
+{
+ return readl(a3700_spi->base + offset);
+}
+
+static void spireg_write(struct a3700_spi *a3700_spi, u32 offset, u32 data)
+{
+ writel(data, a3700_spi->base + offset);
+}
+
+static void a3700_spi_auto_cs_unset(struct a3700_spi *a3700_spi)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val &= ~A3700_SPI_AUTO_CS;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+}
+
+static void a3700_spi_activate_cs(struct a3700_spi *a3700_spi, unsigned int cs)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG);
+ val |= (A3700_SPI_EN << cs);
+ spireg_write(a3700_spi, A3700_SPI_IF_CTRL_REG, val);
+}
+
+static void a3700_spi_deactivate_cs(struct a3700_spi *a3700_spi,
+ unsigned int cs)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG);
+ val &= ~(A3700_SPI_EN << cs);
+ spireg_write(a3700_spi, A3700_SPI_IF_CTRL_REG, val);
+}
+
+static int a3700_spi_pin_mode_set(struct a3700_spi *a3700_spi,
+ unsigned int pin_mode)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val &= ~(A3700_SPI_INST_PIN | A3700_SPI_ADDR_PIN);
+ val &= ~(A3700_SPI_DATA_PIN0 | A3700_SPI_DATA_PIN1);
+
+ switch (pin_mode) {
+ case 1:
+ break;
+ case 2:
+ val |= A3700_SPI_DATA_PIN0;
+ break;
+ case 4:
+ val |= A3700_SPI_DATA_PIN1;
+ break;
+ default:
+ dev_err(&a3700_spi->master->dev, "wrong pin mode %u", pin_mode);
+ return -EINVAL;
+ }
+
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ return 0;
+}
+
+static void a3700_spi_fifo_mode_set(struct a3700_spi *a3700_spi)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val |= A3700_SPI_FIFO_MODE;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+}
+
+static void a3700_spi_mode_set(struct a3700_spi *a3700_spi,
+ unsigned int mode_bits)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+
+ if (mode_bits & SPI_CPOL)
+ val |= A3700_SPI_CLK_POL;
+ else
+ val &= ~A3700_SPI_CLK_POL;
+
+ if (mode_bits & SPI_CPHA)
+ val |= A3700_SPI_CLK_PHA;
+ else
+ val &= ~A3700_SPI_CLK_PHA;
+
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+}
+
+static void a3700_spi_clock_set(struct a3700_spi *a3700_spi,
+ unsigned int speed_hz, u16 mode)
+{
+ u32 val;
+ u32 prescale;
+
+ prescale = DIV_ROUND_UP(clk_get_rate(a3700_spi->clk), speed_hz);
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val = val & ~A3700_SPI_CLK_PRESCALE_MASK;
+
+ val = val | (prescale & A3700_SPI_CLK_PRESCALE_MASK);
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ if (prescale <= 2) {
+ val = spireg_read(a3700_spi, A3700_SPI_IF_TIME_REG);
+ val |= A3700_SPI_CLK_CAPT_EDGE;
+ spireg_write(a3700_spi, A3700_SPI_IF_TIME_REG, val);
+ }
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val &= ~(A3700_SPI_CLK_POL | A3700_SPI_CLK_PHA);
+
+ if (mode & SPI_CPOL)
+ val |= A3700_SPI_CLK_POL;
+
+ if (mode & SPI_CPHA)
+ val |= A3700_SPI_CLK_PHA;
+
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+}
+
+static void a3700_spi_bytelen_set(struct a3700_spi *a3700_spi, unsigned int len)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ if (len == 4)
+ val |= A3700_SPI_BYTE_LEN;
+ else
+ val &= ~A3700_SPI_BYTE_LEN;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ a3700_spi->byte_len = len;
+}
+
+static int a3700_spi_fifo_flush(struct a3700_spi *a3700_spi)
+{
+ int timeout = A3700_SPI_TIMEOUT;
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val |= A3700_SPI_FIFO_FLUSH;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ while (--timeout) {
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ if (!(val & A3700_SPI_FIFO_FLUSH))
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int a3700_spi_init(struct a3700_spi *a3700_spi)
+{
+ struct spi_master *master = a3700_spi->master;
+ u32 val;
+ int i, ret = 0;
+
+ /* Reset SPI unit */
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val |= A3700_SPI_SRST;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ udelay(A3700_SPI_TIMEOUT);
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val &= ~A3700_SPI_SRST;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ /* Disable AUTO_CS and deactivate all chip-selects */
+ a3700_spi_auto_cs_unset(a3700_spi);
+ for (i = 0; i < master->num_chipselect; i++)
+ a3700_spi_deactivate_cs(a3700_spi, i);
+
+ /* Enable FIFO mode */
+ a3700_spi_fifo_mode_set(a3700_spi);
+
+ /* Set SPI mode */
+ a3700_spi_mode_set(a3700_spi, master->mode_bits);
+
+ /* Reset counters */
+ spireg_write(a3700_spi, A3700_SPI_IF_HDR_CNT_REG, 0);
+ spireg_write(a3700_spi, A3700_SPI_IF_DIN_CNT_REG, 0);
+
+ /* Mask the interrupts and clear cause bits */
+ spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0);
+ spireg_write(a3700_spi, A3700_SPI_INT_STAT_REG, ~0U);
+
+ return ret;
+}
+
+static irqreturn_t a3700_spi_interrupt(int irq, void *dev_id)
+{
+ struct spi_master *master = dev_id;
+ struct a3700_spi *a3700_spi;
+ u32 cause;
+
+ a3700_spi = spi_master_get_devdata(master);
+
+ /* Get interrupt causes */
+ cause = spireg_read(a3700_spi, A3700_SPI_INT_STAT_REG);
+
+ if (!cause || !(a3700_spi->wait_mask & cause))
+ return IRQ_NONE;
+
+ /* mask and acknowledge the SPI interrupts */
+ spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0);
+ spireg_write(a3700_spi, A3700_SPI_INT_STAT_REG, cause);
+
+ /* Wake up the transfer */
+ if (a3700_spi->wait_mask & cause)
+ complete(&a3700_spi->done);
+
+ return IRQ_HANDLED;
+}
+
+static bool a3700_spi_wait_completion(struct spi_device *spi)
+{
+ struct a3700_spi *a3700_spi;
+ unsigned int timeout;
+ unsigned int ctrl_reg;
+ unsigned long timeout_jiffies;
+
+ a3700_spi = spi_master_get_devdata(spi->master);
+
+ /* SPI interrupt is edge-triggered, which means an interrupt will
+ * be generated only when detecting a specific status bit changed
+ * from '0' to '1'. So when we start waiting for a interrupt, we
+ * need to check status bit in control reg first, if it is already 1,
+ * then we do not need to wait for interrupt
+ */
+ ctrl_reg = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG);
+ if (a3700_spi->wait_mask & ctrl_reg)
+ return true;
+
+ reinit_completion(&a3700_spi->done);
+
+ spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG,
+ a3700_spi->wait_mask);
+
+ timeout_jiffies = msecs_to_jiffies(A3700_SPI_TIMEOUT);
+ timeout = wait_for_completion_timeout(&a3700_spi->done,
+ timeout_jiffies);
+
+ a3700_spi->wait_mask = 0;
+
+ if (timeout)
+ return true;
+
+ /* there might be the case that right after we checked the
+ * status bits in this routine and before start to wait for
+ * interrupt by wait_for_completion_timeout, the interrupt
+ * happens, to avoid missing it we need to double check
+ * status bits in control reg, if it is already 1, then
+ * consider that we have the interrupt successfully and
+ * return true.
+ */
+ ctrl_reg = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG);
+ if (a3700_spi->wait_mask & ctrl_reg)
+ return true;
+
+ spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0);
+
+ return true;
+}
+
+static bool a3700_spi_transfer_wait(struct spi_device *spi,
+ unsigned int bit_mask)
+{
+ struct a3700_spi *a3700_spi;
+
+ a3700_spi = spi_master_get_devdata(spi->master);
+ a3700_spi->wait_mask = bit_mask;
+
+ return a3700_spi_wait_completion(spi);
+}
+
+static void a3700_spi_fifo_thres_set(struct a3700_spi *a3700_spi,
+ unsigned int bytes)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val &= ~(A3700_SPI_FIFO_THRS_MASK << A3700_SPI_RFIFO_THRS_BIT);
+ val |= (bytes - 1) << A3700_SPI_RFIFO_THRS_BIT;
+ val &= ~(A3700_SPI_FIFO_THRS_MASK << A3700_SPI_WFIFO_THRS_BIT);
+ val |= (7 - bytes) << A3700_SPI_WFIFO_THRS_BIT;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+}
+
+static void a3700_spi_transfer_setup(struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct a3700_spi *a3700_spi;
+ unsigned int byte_len;
+
+ a3700_spi = spi_master_get_devdata(spi->master);
+
+ a3700_spi_clock_set(a3700_spi, xfer->speed_hz, spi->mode);
+
+ byte_len = xfer->bits_per_word >> 3;
+
+ a3700_spi_fifo_thres_set(a3700_spi, byte_len);
+}
+
+static void a3700_spi_set_cs(struct spi_device *spi, bool enable)
+{
+ struct a3700_spi *a3700_spi = spi_master_get_devdata(spi->master);
+
+ if (!enable)
+ a3700_spi_activate_cs(a3700_spi, spi->chip_select);
+ else
+ a3700_spi_deactivate_cs(a3700_spi, spi->chip_select);
+}
+
+static void a3700_spi_header_set(struct a3700_spi *a3700_spi)
+{
+ u32 instr_cnt = 0, addr_cnt = 0, dummy_cnt = 0;
+ u32 val = 0;
+
+ /* Clear the header registers */
+ spireg_write(a3700_spi, A3700_SPI_IF_INST_REG, 0);
+ spireg_write(a3700_spi, A3700_SPI_IF_ADDR_REG, 0);
+ spireg_write(a3700_spi, A3700_SPI_IF_RMODE_REG, 0);
+
+ /* Set header counters */
+ if (a3700_spi->tx_buf) {
+ if (a3700_spi->buf_len <= a3700_spi->instr_cnt) {
+ instr_cnt = a3700_spi->buf_len;
+ } else if (a3700_spi->buf_len <= (a3700_spi->instr_cnt +
+ a3700_spi->addr_cnt)) {
+ instr_cnt = a3700_spi->instr_cnt;
+ addr_cnt = a3700_spi->buf_len - instr_cnt;
+ } else if (a3700_spi->buf_len <= a3700_spi->hdr_cnt) {
+ instr_cnt = a3700_spi->instr_cnt;
+ addr_cnt = a3700_spi->addr_cnt;
+ /* Need to handle the normal write case with 1 byte
+ * data
+ */
+ if (!a3700_spi->tx_buf[instr_cnt + addr_cnt])
+ dummy_cnt = a3700_spi->buf_len - instr_cnt -
+ addr_cnt;
+ }
+ val |= ((instr_cnt & A3700_SPI_INSTR_CNT_MASK)
+ << A3700_SPI_INSTR_CNT_BIT);
+ val |= ((addr_cnt & A3700_SPI_ADDR_CNT_MASK)
+ << A3700_SPI_ADDR_CNT_BIT);
+ val |= ((dummy_cnt & A3700_SPI_DUMMY_CNT_MASK)
+ << A3700_SPI_DUMMY_CNT_BIT);
+ }
+ spireg_write(a3700_spi, A3700_SPI_IF_HDR_CNT_REG, val);
+
+ /* Update the buffer length to be transferred */
+ a3700_spi->buf_len -= (instr_cnt + addr_cnt + dummy_cnt);
+
+ /* Set Instruction */
+ val = 0;
+ while (instr_cnt--) {
+ val = (val << 8) | a3700_spi->tx_buf[0];
+ a3700_spi->tx_buf++;
+ }
+ spireg_write(a3700_spi, A3700_SPI_IF_INST_REG, val);
+
+ /* Set Address */
+ val = 0;
+ while (addr_cnt--) {
+ val = (val << 8) | a3700_spi->tx_buf[0];
+ a3700_spi->tx_buf++;
+ }
+ spireg_write(a3700_spi, A3700_SPI_IF_ADDR_REG, val);
+}
+
+static int a3700_is_wfifo_full(struct a3700_spi *a3700_spi)
+{
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG);
+ return (val & A3700_SPI_WFIFO_FULL);
+}
+
+static int a3700_spi_fifo_write(struct a3700_spi *a3700_spi)
+{
+ u32 val;
+ int i = 0;
+
+ while (!a3700_is_wfifo_full(a3700_spi) && a3700_spi->buf_len) {
+ val = 0;
+ if (a3700_spi->buf_len >= 4) {
+ val = cpu_to_le32(*(u32 *)a3700_spi->tx_buf);
+ spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, val);
+
+ a3700_spi->buf_len -= 4;
+ a3700_spi->tx_buf += 4;
+ } else {
+ /*
+ * If the remained buffer length is less than 4-bytes,
+ * we should pad the write buffer with all ones. So that
+ * it avoids overwrite the unexpected bytes following
+ * the last one.
+ */
+ val = GENMASK(31, 0);
+ while (a3700_spi->buf_len) {
+ val &= ~(0xff << (8 * i));
+ val |= *a3700_spi->tx_buf++ << (8 * i);
+ i++;
+ a3700_spi->buf_len--;
+
+ spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG,
+ val);
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int a3700_is_rfifo_empty(struct a3700_spi *a3700_spi)
+{
+ u32 val = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG);
+
+ return (val & A3700_SPI_RFIFO_EMPTY);
+}
+
+static int a3700_spi_fifo_read(struct a3700_spi *a3700_spi)
+{
+ u32 val;
+
+ while (!a3700_is_rfifo_empty(a3700_spi) && a3700_spi->buf_len) {
+ val = spireg_read(a3700_spi, A3700_SPI_DATA_IN_REG);
+ if (a3700_spi->buf_len >= 4) {
+ u32 data = le32_to_cpu(val);
+ memcpy(a3700_spi->rx_buf, &data, 4);
+
+ a3700_spi->buf_len -= 4;
+ a3700_spi->rx_buf += 4;
+ } else {
+ /*
+ * When remain bytes is not larger than 4, we should
+ * avoid memory overwriting and just write the left rx
+ * buffer bytes.
+ */
+ while (a3700_spi->buf_len) {
+ *a3700_spi->rx_buf = val & 0xff;
+ val >>= 8;
+
+ a3700_spi->buf_len--;
+ a3700_spi->rx_buf++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void a3700_spi_transfer_abort_fifo(struct a3700_spi *a3700_spi)
+{
+ int timeout = A3700_SPI_TIMEOUT;
+ u32 val;
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val |= A3700_SPI_XFER_STOP;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ while (--timeout) {
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ if (!(val & A3700_SPI_XFER_START))
+ break;
+ udelay(1);
+ }
+
+ a3700_spi_fifo_flush(a3700_spi);
+
+ val &= ~A3700_SPI_XFER_STOP;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+}
+
+static int a3700_spi_prepare_message(struct spi_master *master,
+ struct spi_message *message)
+{
+ struct a3700_spi *a3700_spi = spi_master_get_devdata(master);
+ struct spi_device *spi = message->spi;
+ int ret;
+
+ ret = clk_enable(a3700_spi->clk);
+ if (ret) {
+ dev_err(&spi->dev, "failed to enable clk with error %d\n", ret);
+ return ret;
+ }
+
+ /* Flush the FIFOs */
+ ret = a3700_spi_fifo_flush(a3700_spi);
+ if (ret)
+ return ret;
+
+ a3700_spi_bytelen_set(a3700_spi, 4);
+
+ return 0;
+}
+
+static int a3700_spi_transfer_one(struct spi_master *master,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct a3700_spi *a3700_spi = spi_master_get_devdata(master);
+ int ret = 0, timeout = A3700_SPI_TIMEOUT;
+ unsigned int nbits = 0;
+ u32 val;
+
+ a3700_spi_transfer_setup(spi, xfer);
+
+ a3700_spi->tx_buf = xfer->tx_buf;
+ a3700_spi->rx_buf = xfer->rx_buf;
+ a3700_spi->buf_len = xfer->len;
+
+ /* SPI transfer headers */
+ a3700_spi_header_set(a3700_spi);
+
+ if (xfer->tx_buf)
+ nbits = xfer->tx_nbits;
+ else if (xfer->rx_buf)
+ nbits = xfer->rx_nbits;
+
+ a3700_spi_pin_mode_set(a3700_spi, nbits);
+
+ if (xfer->rx_buf) {
+ /* Set read data length */
+ spireg_write(a3700_spi, A3700_SPI_IF_DIN_CNT_REG,
+ a3700_spi->buf_len);
+ /* Start READ transfer */
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val &= ~A3700_SPI_RW_EN;
+ val |= A3700_SPI_XFER_START;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+ } else if (xfer->tx_buf) {
+ /* Start Write transfer */
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val |= (A3700_SPI_XFER_START | A3700_SPI_RW_EN);
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+
+ /*
+ * If there are data to be written to the SPI device, xmit_data
+ * flag is set true; otherwise the instruction in SPI_INSTR does
+ * not require data to be written to the SPI device, then
+ * xmit_data flag is set false.
+ */
+ a3700_spi->xmit_data = (a3700_spi->buf_len != 0);
+ }
+
+ while (a3700_spi->buf_len) {
+ if (a3700_spi->tx_buf) {
+ /* Wait wfifo ready */
+ if (!a3700_spi_transfer_wait(spi,
+ A3700_SPI_WFIFO_RDY)) {
+ dev_err(&spi->dev,
+ "wait wfifo ready timed out\n");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+ /* Fill up the wfifo */
+ ret = a3700_spi_fifo_write(a3700_spi);
+ if (ret)
+ goto error;
+ } else if (a3700_spi->rx_buf) {
+ /* Wait rfifo ready */
+ if (!a3700_spi_transfer_wait(spi,
+ A3700_SPI_RFIFO_RDY)) {
+ dev_err(&spi->dev,
+ "wait rfifo ready timed out\n");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+ /* Drain out the rfifo */
+ ret = a3700_spi_fifo_read(a3700_spi);
+ if (ret)
+ goto error;
+ }
+ }
+
+ /*
+ * Stop a write transfer in fifo mode:
+ * - wait all the bytes in wfifo to be shifted out
+ * - set XFER_STOP bit
+ * - wait XFER_START bit clear
+ * - clear XFER_STOP bit
+ * Stop a read transfer in fifo mode:
+ * - the hardware is to reset the XFER_START bit
+ * after the number of bytes indicated in DIN_CNT
+ * register
+ * - just wait XFER_START bit clear
+ */
+ if (a3700_spi->tx_buf) {
+ if (a3700_spi->xmit_data) {
+ /*
+ * If there are data written to the SPI device, wait
+ * until SPI_WFIFO_EMPTY is 1 to wait for all data to
+ * transfer out of write FIFO.
+ */
+ if (!a3700_spi_transfer_wait(spi,
+ A3700_SPI_WFIFO_EMPTY)) {
+ dev_err(&spi->dev, "wait wfifo empty timed out\n");
+ return -ETIMEDOUT;
+ }
+ } else {
+ /*
+ * If the instruction in SPI_INSTR does not require data
+ * to be written to the SPI device, wait until SPI_RDY
+ * is 1 for the SPI interface to be in idle.
+ */
+ if (!a3700_spi_transfer_wait(spi, A3700_SPI_XFER_RDY)) {
+ dev_err(&spi->dev, "wait xfer ready timed out\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ val |= A3700_SPI_XFER_STOP;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+ }
+
+ while (--timeout) {
+ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
+ if (!(val & A3700_SPI_XFER_START))
+ break;
+ udelay(1);
+ }
+
+ if (timeout == 0) {
+ dev_err(&spi->dev, "wait transfer start clear timed out\n");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ val &= ~A3700_SPI_XFER_STOP;
+ spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val);
+ goto out;
+
+error:
+ a3700_spi_transfer_abort_fifo(a3700_spi);
+out:
+ spi_finalize_current_transfer(master);
+
+ return ret;
+}
+
+static int a3700_spi_unprepare_message(struct spi_master *master,
+ struct spi_message *message)
+{
+ struct a3700_spi *a3700_spi = spi_master_get_devdata(master);
+
+ clk_disable(a3700_spi->clk);
+
+ return 0;
+}
+
+static const struct of_device_id a3700_spi_dt_ids[] = {
+ { .compatible = "marvell,armada-3700-spi", .data = NULL },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, a3700_spi_dt_ids);
+
+static int a3700_spi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *of_node = dev->of_node;
+ struct resource *res;
+ struct spi_master *master;
+ struct a3700_spi *spi;
+ u32 num_cs = 0;
+ int ret = 0;
+
+ master = spi_alloc_master(dev, sizeof(*spi));
+ if (!master) {
+ dev_err(dev, "master allocation failed\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (of_property_read_u32(of_node, "num-cs", &num_cs)) {
+ dev_err(dev, "could not find num-cs\n");
+ ret = -ENXIO;
+ goto error;
+ }
+
+ master->bus_num = pdev->id;
+ master->dev.of_node = of_node;
+ master->mode_bits = SPI_MODE_3;
+ master->num_chipselect = num_cs;
+ master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(32);
+ master->prepare_message = a3700_spi_prepare_message;
+ master->transfer_one = a3700_spi_transfer_one;
+ master->unprepare_message = a3700_spi_unprepare_message;
+ master->set_cs = a3700_spi_set_cs;
+ master->flags = SPI_MASTER_HALF_DUPLEX;
+ master->mode_bits |= (SPI_RX_DUAL | SPI_RX_DUAL |
+ SPI_RX_QUAD | SPI_TX_QUAD);
+
+ platform_set_drvdata(pdev, master);
+
+ spi = spi_master_get_devdata(master);
+ memset(spi, 0, sizeof(struct a3700_spi));
+
+ spi->master = master;
+ spi->instr_cnt = A3700_INSTR_CNT;
+ spi->addr_cnt = A3700_ADDR_CNT;
+ spi->hdr_cnt = A3700_INSTR_CNT + A3700_ADDR_CNT +
+ A3700_DUMMY_CNT;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ spi->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(spi->base)) {
+ ret = PTR_ERR(spi->base);
+ goto error;
+ }
+
+ spi->irq = platform_get_irq(pdev, 0);
+ if (spi->irq < 0) {
+ dev_err(dev, "could not get irq: %d\n", spi->irq);
+ ret = -ENXIO;
+ goto error;
+ }
+
+ init_completion(&spi->done);
+
+ spi->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(spi->clk)) {
+ dev_err(dev, "could not find clk: %ld\n", PTR_ERR(spi->clk));
+ goto error;
+ }
+
+ ret = clk_prepare(spi->clk);
+ if (ret) {
+ dev_err(dev, "could not prepare clk: %d\n", ret);
+ goto error;
+ }
+
+ ret = a3700_spi_init(spi);
+ if (ret)
+ goto error_clk;
+
+ ret = devm_request_irq(dev, spi->irq, a3700_spi_interrupt, 0,
+ dev_name(dev), master);
+ if (ret) {
+ dev_err(dev, "could not request IRQ: %d\n", ret);
+ goto error_clk;
+ }
+
+ ret = devm_spi_register_master(dev, master);
+ if (ret) {
+ dev_err(dev, "Failed to register master\n");
+ goto error_clk;
+ }
+
+ return 0;
+
+error_clk:
+ clk_disable_unprepare(spi->clk);
+error:
+ spi_master_put(master);
+out:
+ return ret;
+}
+
+static int a3700_spi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct a3700_spi *spi = spi_master_get_devdata(master);
+
+ clk_unprepare(spi->clk);
+ spi_master_put(master);
+
+ return 0;
+}
+
+static struct platform_driver a3700_spi_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(a3700_spi_dt_ids),
+ },
+ .probe = a3700_spi_probe,
+ .remove = a3700_spi_remove,
+};
+
+module_platform_driver(a3700_spi_driver);
+
+MODULE_DESCRIPTION("Armada-3700 SPI driver");
+MODULE_AUTHOR("Wilson Ding <dingwei@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
--
2.10.2
^ permalink raw reply related
* Applied "spi: armada-3700: Add documentation for the Armada 3700 SPI Controller" to the spi tree
From: Mark Brown @ 2016-12-08 16:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161129143939.3191-4-romain.perier@free-electrons.com>
The patch
spi: armada-3700: Add documentation for the Armada 3700 SPI Controller
has been applied to the spi tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From 4049537742b3ed39fac4da10d31f3171a2ee9a3e Mon Sep 17 00:00:00 2001
From: Romain Perier <romain.perier@free-electrons.com>
Date: Thu, 8 Dec 2016 15:58:45 +0100
Subject: [PATCH] spi: armada-3700: Add documentation for the Armada 3700 SPI
Controller
This adds the devicetree bindings documentation for the SPI controller
present in the Marvell Armada 3700 SoCs.
Signed-off-by: Romain Perier <romain.perier@free-electrons.com>
Tested-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
.../devicetree/bindings/spi/spi-armada-3700.txt | 25 ++++++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/spi-armada-3700.txt
diff --git a/Documentation/devicetree/bindings/spi/spi-armada-3700.txt b/Documentation/devicetree/bindings/spi/spi-armada-3700.txt
new file mode 100644
index 000000000000..1564aa8c02cd
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi-armada-3700.txt
@@ -0,0 +1,25 @@
+* Marvell Armada 3700 SPI Controller
+
+Required Properties:
+
+- compatible: should be "marvell,armada-3700-spi"
+- reg: physical base address of the controller and length of memory mapped
+ region.
+- interrupts: The interrupt number. The interrupt specifier format depends on
+ the interrupt controller and of its driver.
+- clocks: Must contain the clock source, usually from the North Bridge clocks.
+- num-cs: The number of chip selects that is supported by this SPI Controller
+- #address-cells: should be 1.
+- #size-cells: should be 0.
+
+Example:
+
+ spi0: spi at 10600 {
+ compatible = "marvell,armada-3700-spi";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x10600 0x5d>;
+ clocks = <&nb_perih_clk 7>;
+ interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+ num-cs = <4>;
+ };
--
2.10.2
^ permalink raw reply related
* [RFC v3 00/10] KVM PCIe/MSI passthrough on ARM/ARM64 and IOVA reserved regions
From: Alex Williamson @ 2016-12-08 17:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cd16fc5c-8649-0bfa-d67d-8f257aa38bd6@arm.com>
On Thu, 8 Dec 2016 13:14:04 +0000
Robin Murphy <robin.murphy@arm.com> wrote:
> On 08/12/16 09:36, Auger Eric wrote:
> > 3) RMRR reporting in the iommu group sysfs? Joerg: yes; Don: no
> > My current series does not expose them in iommu group sysfs.
> > I understand we can expose the RMRR regions in the iomm group sysfs
> > without necessarily supporting RMRR requiring device assignment.
> > We can also add this support later.
>
> As you say, reporting them doesn't necessitate allowing device
> assignment, and it's information which can already be easily grovelled
> out of dmesg (for intel-iommu at least) - there doesn't seem to be any
> need to hide them, but the x86 folks can have the final word on that.
Eric and I talked about this and I don't see the value in identifying
an RMRR as anything other than a reserved range for a device. It's not
userspace's job to maintain an identify mapped range for the device,
and it can't be trusted to do so anyway. It does throw a kink in the
machinery though as an RMRR is a reserved memory range unique to a
device. It doesn't really fit into a monolithic /sys/class/iommu view
of global reserved regions as an RMRR is only relevant to the device
paths affected.
Another kink is that sometimes we know what the RMRR is for, know that
it's irrelevant for our use case, and ignore it. This is true for USB
and Intel graphics use cases of RMRRs.
Also, aside from the above mentioned cases, devices with RMRRs are
currently excluded from participating in the IOMMU API by the
intel-iommu driver and I expect this to continue in the general case
regardless of whether the ranges are more easily exposed to userspace.
ARM may have to deal with mangling a guest memory map due to lack of
any standard layout, de facto or otherwise, but for x86 I don't think
it's worth the migration and hotplug implications. Thanks,
Alex
^ permalink raw reply
* [RFC PATCH v3 07/11] arm64: compat: Add a 32-bit vDSO
From: Nathan Lynch @ 2016-12-08 17:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <0707aecb-6638-2447-cf4a-9c30fc08dae5@arm.com>
Kevin Brodsky <kevin.brodsky@arm.com> writes:
> On 07/12/16 18:51, Nathan Lynch wrote:
>> Kevin Brodsky <kevin.brodsky@arm.com> writes:
>>> diff --git a/arch/arm64/kernel/vdso32/vgettimeofday.c b/arch/arm64/kernel/vdso32/vgettimeofday.c
>>> new file mode 100644
>>> index 000000000000..53c3d1f82b26
>>> --- /dev/null
>>> +++ b/arch/arm64/kernel/vdso32/vgettimeofday.c
>> As I said at LPC last month, I'm not excited to have arch/arm's
>> vgettimeofday.c copied into arch/arm64 and tweaked; I'd rather see this
>> part of the implementation shared between arch/arm and arch/arm64
>> somehow, even if there's not an elegant way to do so.
>>
>> The situation which must be avoided is one where the arch/arm64 compat
>> VDSO incompatibly diverges from the arch/arm VDSO. That becomes much
>> less likely if there's only one copy of the userspace-exposed code to
>> maintain.
>
> I still agree this is very suboptimal. However, I also think this is by far the most
> straightforward solution, and I would like to stick to it *as a first step*. If you
> diff this "tweaked" vgettimeofday.c with the original one, you'll see that it is
> really not obvious to share even parts of vgettimeofday.c in the current state of arm
> and arm64.
After adapting your get_vdso_data() to arch/arm/vdso, I come up with the
differences below, which seems surmountable mostly with the addition of
appropriate accessor functions for the data page, or changing the field
names to match. So I can't say I'm persuaded.
I'm happy to review and facilitate changes to code in arch/arm/vdso to
make it possible to share it with arm64 for its compat VDSO.
vgettimeofday.c | 84 ++++++++++++++++++++++++--------------------------------
1 file changed, 37 insertions(+), 47 deletions(-)
--- arch/arm/vdso/vgettimeofday.c 2016-12-07 11:24:35.043856904 -0600
+++ kb-vgettimeofday.c 2016-12-08 10:02:14.031896779 -0600
@@ -15,20 +15,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/clocksource.h>
#include <linux/compiler.h>
-#include <linux/hrtimer.h>
#include <linux/time.h>
-#include <asm/arch_timer.h>
-#include <asm/barrier.h>
-#include <asm/bug.h>
-#include <asm/page.h>
#include <asm/unistd.h>
#include <asm/vdso_datapage.h>
-#ifndef CONFIG_AEABI
-#error This code depends on AEABI system call conventions
-#endif
+#include "aarch32-barrier.h"
+/*
+ * We use the hidden visibility to prevent the compiler from generating a GOT
+ * relocation. Not only is going through a GOT useless (the entry couldn't and
+ * musn't be overridden by another library), it does not even work: the linker
+ * cannot generate an absolute address to the data page.
+ *
+ * With the hidden visibility, the compiler simply generates a PC-relative
+ * relocation (R_ARM_REL32), and this is what we need.
+ */
extern const struct vdso_data _vdso_data __attribute__((visibility("hidden")));
static inline const struct vdso_data *get_vdso_data(void)
@@ -52,13 +55,11 @@
return ret;
}
-#define __get_datapage() get_vdso_data()
-
static notrace u32 __vdso_read_begin(const struct vdso_data *vdata)
{
u32 seq;
repeat:
- seq = ACCESS_ONCE(vdata->seq_count);
+ seq = ACCESS_ONCE(vdata->tb_seq_count);
if (seq & 1) {
cpu_relax();
goto repeat;
@@ -72,26 +73,30 @@
seq = __vdso_read_begin(vdata);
- smp_rmb(); /* Pairs with smp_wmb in vdso_write_end */
+ aarch32_smp_rmb();
return seq;
}
static notrace int vdso_read_retry(const struct vdso_data *vdata, u32 start)
{
- smp_rmb(); /* Pairs with smp_wmb in vdso_write_begin */
- return vdata->seq_count != start;
+ aarch32_smp_rmb();
+ return vdata->tb_seq_count != start;
}
+/*
+ * Note: only AEABI is supported by the compat layer, we can assume AEABI
+ * syscall conventions are used.
+ */
static notrace long clock_gettime_fallback(clockid_t _clkid,
struct timespec *_ts)
{
register struct timespec *ts asm("r1") = _ts;
register clockid_t clkid asm("r0") = _clkid;
register long ret asm ("r0");
- register long nr asm("r7") = __NR_clock_gettime;
+ register long nr asm("r7") = __NR_compat_clock_gettime;
asm volatile(
- " swi #0\n"
+ " svc #0\n"
: "=r" (ret)
: "r" (clkid), "r" (ts), "r" (nr)
: "memory");
@@ -138,25 +143,27 @@
return 0;
}
-#ifdef CONFIG_ARM_ARCH_TIMER
-
static notrace u64 get_ns(const struct vdso_data *vdata)
{
u64 cycle_delta;
u64 cycle_now;
u64 nsec;
- cycle_now = arch_counter_get_cntvct();
+ /* AArch32 implementation of arch_counter_get_cntvct() */
+ isb();
+ asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (cycle_now));
- cycle_delta = (cycle_now - vdata->cs_cycle_last) & vdata->cs_mask;
+ /* The virtual counter provides 56 significant bits. */
+ cycle_delta = (cycle_now - vdata->cs_cycle_last) & CLOCKSOURCE_MASK(56);
- nsec = (cycle_delta * vdata->cs_mult) + vdata->xtime_clock_snsec;
+ nsec = (cycle_delta * vdata->cs_mono_mult) + vdata->xtime_clock_nsec;
nsec >>= vdata->cs_shift;
return nsec;
}
-static notrace int do_realtime(struct timespec *ts, const struct vdso_data *vdata)
+static notrace int do_realtime(struct timespec *ts,
+ const struct vdso_data *vdata)
{
u64 nsecs;
u32 seq;
@@ -164,7 +171,7 @@
do {
seq = vdso_read_begin(vdata);
- if (!vdata->tk_is_cntvct)
+ if (vdata->use_syscall)
return -1;
ts->tv_sec = vdata->xtime_clock_sec;
@@ -178,7 +185,8 @@
return 0;
}
-static notrace int do_monotonic(struct timespec *ts, const struct vdso_data *vdata)
+static notrace int do_monotonic(struct timespec *ts,
+ const struct vdso_data *vdata)
{
struct timespec tomono;
u64 nsecs;
@@ -187,7 +195,7 @@
do {
seq = vdso_read_begin(vdata);
- if (!vdata->tk_is_cntvct)
+ if (vdata->use_syscall)
return -1;
ts->tv_sec = vdata->xtime_clock_sec;
@@ -205,27 +213,11 @@
return 0;
}
-#else /* CONFIG_ARM_ARCH_TIMER */
-
-static notrace int do_realtime(struct timespec *ts, const struct vdso_data *vdata)
-{
- return -1;
-}
-
-static notrace int do_monotonic(struct timespec *ts, const struct vdso_data *vdata)
-{
- return -1;
-}
-
-#endif /* CONFIG_ARM_ARCH_TIMER */
-
notrace int __vdso_clock_gettime(clockid_t clkid, struct timespec *ts)
{
- const struct vdso_data *vdata;
+ const struct vdso_data *vdata = get_vdso_data();
int ret = -1;
- vdata = __get_datapage();
-
switch (clkid) {
case CLOCK_REALTIME_COARSE:
ret = do_realtime_coarse(ts, vdata);
@@ -255,10 +247,10 @@
register struct timezone *tz asm("r1") = _tz;
register struct timeval *tv asm("r0") = _tv;
register long ret asm ("r0");
- register long nr asm("r7") = __NR_gettimeofday;
+ register long nr asm("r7") = __NR_compat_gettimeofday;
asm volatile(
- " swi #0\n"
+ " svc #0\n"
: "=r" (ret)
: "r" (tv), "r" (tz), "r" (nr)
: "memory");
@@ -269,11 +261,9 @@
notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
{
struct timespec ts;
- const struct vdso_data *vdata;
+ const struct vdso_data *vdata = get_vdso_data();
int ret;
- vdata = __get_datapage();
-
ret = do_realtime(&ts, vdata);
if (ret)
return gettimeofday_fallback(tv, tz);
^ permalink raw reply
* [PATCH 0/3] rtc: armada38x: Few improvement and cleanup
From: Gregory CLEMENT @ 2016-12-08 17:10 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
this series brings some improvement and cleanup for the armada 38x
RTC.
The errata for this RTC gave us more information on how to work around
it. The first patch implement it.
The second patch convert the driver to the time64_t usage.
And the last patch make the driver really usable on 64 bits
architecture.
These last two patches are a preliminary step allowing using this
driver on the Armada 7K/8K which are 64 bits ARM SoCs which use the
same RTC IP.
Thanks,
Gregory
Gregory CLEMENT (2):
rtc: armada38x: Convert to time64_t
rtc: armada38x: Prepare for being use on 64 bits
Shaker Daibes (1):
rtc: armada38x: improve RTC errata implementation
drivers/rtc/rtc-armada38x.c | 145 ++++++++++++++++++++++++++++++--------------
1 file changed, 100 insertions(+), 45 deletions(-)
--
2.10.2
^ permalink raw reply
* [PATCH 1/3] rtc: armada38x: improve RTC errata implementation
From: Gregory CLEMENT @ 2016-12-08 17:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208171010.29446-1-gregory.clement@free-electrons.com>
From: Shaker Daibes <shaker@marvell.com>
According to FE-3124064:
The device supports CPU write and read access to the RTC time register.
However, due to this errata, read from RTC TIME register may fail.
Workaround:
General configuration:
1. Configure the RTC Mbus Bridge Timing Control register (offset 0x184A0)
to value 0xFD4D4FFF
Write RTC WRCLK Period to its maximum value (0x3FF)
Write RTC WRCLK setup to 0x53 (default value )
Write RTC WRCLK High Time to 0x53 (default value )
Write RTC Read Output Delay to its maximum value (0x1F)
Mbus - Read All Byte Enable to 0x1 (default value )
2. Configure the RTC Test Configuration Register (offset 0xA381C) bit3
to '1' (Reserved, Marvell internal)
For any RTC register read operation:
1. Read the requested register 100 times.
2. Find the result that appears most frequently and use this result
as the correct value.
For any RTC register write operation:
1. Issue two dummy writes of 0x0 to the RTC Status register (offset
0xA3800).
2. Write the time to the RTC Time register (offset 0xA380C).
[gregory.clement at free-electrons.com: cosmetic changes and fix issues for
interrupt in original patch]
Reviewed-by: Lior Amsalem <alior@marvell.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
drivers/rtc/rtc-armada38x.c | 109 ++++++++++++++++++++++++++++++++++----------
1 file changed, 85 insertions(+), 24 deletions(-)
diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c
index 9a3f2a6f512e..a0859286a4c4 100644
--- a/drivers/rtc/rtc-armada38x.c
+++ b/drivers/rtc/rtc-armada38x.c
@@ -29,12 +29,21 @@
#define RTC_TIME 0xC
#define RTC_ALARM1 0x10
+#define SOC_RTC_BRIDGE_TIMING_CTL 0x0
+#define SOC_RTC_PERIOD_OFFS 0
+#define SOC_RTC_PERIOD_MASK (0x3FF << SOC_RTC_PERIOD_OFFS)
+#define SOC_RTC_READ_DELAY_OFFS 26
+#define SOC_RTC_READ_DELAY_MASK (0x1F << SOC_RTC_READ_DELAY_OFFS)
+
#define SOC_RTC_INTERRUPT 0x8
#define SOC_RTC_ALARM1 BIT(0)
#define SOC_RTC_ALARM2 BIT(1)
#define SOC_RTC_ALARM1_MASK BIT(2)
#define SOC_RTC_ALARM2_MASK BIT(3)
+
+#define SAMPLE_NR 100
+
struct armada38x_rtc {
struct rtc_device *rtc_dev;
void __iomem *regs;
@@ -47,32 +56,85 @@ struct armada38x_rtc {
* According to the datasheet, the OS should wait 5us after every
* register write to the RTC hard macro so that the required update
* can occur without holding off the system bus
+ * According to errata FE-3124064, Write to any RTC register
+ * may fail. As a workaround, before writing to RTC
+ * register, issue a dummy write of 0x0 twice to RTC Status
+ * register.
*/
+
static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset)
{
+ writel(0, rtc->regs + RTC_STATUS);
+ writel(0, rtc->regs + RTC_STATUS);
writel(val, rtc->regs + offset);
udelay(5);
}
+/* Update RTC-MBUS bridge timing parameters */
+static void rtc_update_mbus_timing_params(struct armada38x_rtc *rtc)
+{
+ uint32_t reg;
+
+ reg = readl(rtc->regs_soc + SOC_RTC_BRIDGE_TIMING_CTL);
+ reg &= ~SOC_RTC_PERIOD_MASK;
+ reg |= 0x3FF << SOC_RTC_PERIOD_OFFS; /* Maximum value */
+ reg &= ~SOC_RTC_READ_DELAY_MASK;
+ reg |= 0x1F << SOC_RTC_READ_DELAY_OFFS; /* Maximum value */
+ writel(reg, rtc->regs_soc + SOC_RTC_BRIDGE_TIMING_CTL);
+}
+
+struct str_value_to_freq {
+ unsigned long value;
+ u8 freq;
+} __packed;
+
+static unsigned long read_rtc_register_wa(struct armada38x_rtc *rtc, u8 rtc_reg)
+{
+ unsigned long value_array[SAMPLE_NR], i, j, value;
+ unsigned long max = 0, index_max = SAMPLE_NR - 1;
+ struct str_value_to_freq value_to_freq[SAMPLE_NR];
+
+ for (i = 0; i < SAMPLE_NR; i++) {
+ value_to_freq[i].freq = 0;
+ value_array[i] = readl(rtc->regs + rtc_reg);
+ }
+ for (i = 0; i < SAMPLE_NR; i++) {
+ value = value_array[i];
+ /*
+ * if value appears in value_to_freq array so add the
+ * counter of value, if didn't appear yet in counters
+ * array then allocate new member of value_to_freq
+ * array with counter = 1
+ */
+ for (j = 0; j < SAMPLE_NR; j++) {
+ if (value_to_freq[j].freq == 0 ||
+ value_to_freq[j].value == value)
+ break;
+ if (j == (SAMPLE_NR - 1))
+ break;
+ }
+ if (value_to_freq[j].freq == 0)
+ value_to_freq[j].value = value;
+ value_to_freq[j].freq++;
+ /* find the most common result */
+ if (max < value_to_freq[j].freq) {
+ index_max = j;
+ max = value_to_freq[j].freq;
+ }
+ }
+ return value_to_freq[index_max].value;
+}
+
static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
- unsigned long time, time_check, flags;
+ unsigned long time, flags;
spin_lock_irqsave(&rtc->lock, flags);
- time = readl(rtc->regs + RTC_TIME);
- /*
- * WA for failing time set attempts. As stated in HW ERRATA if
- * more than one second between two time reads is detected
- * then read once again.
- */
- time_check = readl(rtc->regs + RTC_TIME);
- if ((time_check - time) > 1)
- time_check = readl(rtc->regs + RTC_TIME);
-
+ time = read_rtc_register_wa(rtc, RTC_TIME);
spin_unlock_irqrestore(&rtc->lock, flags);
- rtc_time_to_tm(time_check, tm);
+ rtc_time_to_tm(time, tm);
return 0;
}
@@ -87,16 +149,9 @@ static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
if (ret)
goto out;
- /*
- * According to errata FE-3124064, Write to RTC TIME register
- * may fail. As a workaround, after writing to RTC TIME
- * register, issue a dummy write of 0x0 twice to RTC Status
- * register.
- */
+
spin_lock_irqsave(&rtc->lock, flags);
rtc_delayed_write(time, rtc, RTC_TIME);
- rtc_delayed_write(0, rtc, RTC_STATUS);
- rtc_delayed_write(0, rtc, RTC_STATUS);
spin_unlock_irqrestore(&rtc->lock, flags);
out:
@@ -111,8 +166,8 @@ static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
spin_lock_irqsave(&rtc->lock, flags);
- time = readl(rtc->regs + RTC_ALARM1);
- val = readl(rtc->regs + RTC_IRQ1_CONF) & RTC_IRQ1_AL_EN;
+ time = read_rtc_register_wa(rtc, RTC_ALARM1);
+ val = read_rtc_register_wa(rtc, RTC_IRQ1_CONF) & RTC_IRQ1_AL_EN;
spin_unlock_irqrestore(&rtc->lock, flags);
@@ -182,7 +237,7 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT);
- val = readl(rtc->regs + RTC_IRQ1_CONF);
+ val = read_rtc_register_wa(rtc, RTC_IRQ1_CONF);
/* disable all the interrupts for alarm 1 */
rtc_delayed_write(0, rtc, RTC_IRQ1_CONF);
/* Ack the event */
@@ -196,7 +251,6 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
else
event |= RTC_PF;
}
-
rtc_update_irq(rtc->rtc_dev, 1, event);
return IRQ_HANDLED;
@@ -253,6 +307,9 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev)
if (rtc->irq != -1)
device_init_wakeup(&pdev->dev, 1);
+ /* Update RTC-MBUS bridge timing parameters */
+ rtc_update_mbus_timing_params(rtc);
+
rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
&armada38x_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc->rtc_dev)) {
@@ -260,6 +317,7 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
return ret;
}
+
return 0;
}
@@ -280,6 +338,9 @@ static int armada38x_rtc_resume(struct device *dev)
if (device_may_wakeup(dev)) {
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
+ /* Update RTC-MBUS bridge timing parameters */
+ rtc_update_mbus_timing_params(rtc);
+
return disable_irq_wake(rtc->irq);
}
--
2.10.2
^ permalink raw reply related
* [PATCH 2/3] rtc: armada38x: Convert to time64_t
From: Gregory CLEMENT @ 2016-12-08 17:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208171010.29446-1-gregory.clement@free-electrons.com>
It is one more step to remove the deprecated functions rtc_time_to_tm
and rtc_tm_to_time.
And as bonus it simplifies a little the driver.
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
drivers/rtc/rtc-armada38x.c | 42 ++++++++++++++++++------------------------
1 file changed, 18 insertions(+), 24 deletions(-)
diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c
index a0859286a4c4..b8a74ffaae80 100644
--- a/drivers/rtc/rtc-armada38x.c
+++ b/drivers/rtc/rtc-armada38x.c
@@ -128,13 +128,14 @@ static unsigned long read_rtc_register_wa(struct armada38x_rtc *rtc, u8 rtc_reg)
static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
- unsigned long time, flags;
+ unsigned long flags;
+ time64_t time;
spin_lock_irqsave(&rtc->lock, flags);
- time = read_rtc_register_wa(rtc, RTC_TIME);
+ time = (time64_t)read_rtc_register_wa(rtc, RTC_TIME);
spin_unlock_irqrestore(&rtc->lock, flags);
- rtc_time_to_tm(time, tm);
+ rtc_time64_to_tm(time, tm);
return 0;
}
@@ -142,37 +143,34 @@ static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
- int ret = 0;
- unsigned long time, flags;
-
- ret = rtc_tm_to_time(tm, &time);
+ unsigned long flags;
+ time64_t time;
- if (ret)
- goto out;
+ time = rtc_tm_to_time64(tm);
spin_lock_irqsave(&rtc->lock, flags);
- rtc_delayed_write(time, rtc, RTC_TIME);
+ rtc_delayed_write((u32)time, rtc, RTC_TIME);
spin_unlock_irqrestore(&rtc->lock, flags);
-out:
- return ret;
+ return 0;
}
static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
- unsigned long time, flags;
+ unsigned long flags;
+ time64_t time;
u32 val;
spin_lock_irqsave(&rtc->lock, flags);
- time = read_rtc_register_wa(rtc, RTC_ALARM1);
+ time = (time64_t)read_rtc_register_wa(rtc, RTC_ALARM1);
val = read_rtc_register_wa(rtc, RTC_IRQ1_CONF) & RTC_IRQ1_AL_EN;
spin_unlock_irqrestore(&rtc->lock, flags);
alrm->enabled = val ? 1 : 0;
- rtc_time_to_tm(time, &alrm->time);
+ rtc_time64_to_tm(time, &alrm->time);
return 0;
}
@@ -180,18 +178,15 @@ static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
- unsigned long time, flags;
- int ret = 0;
+ unsigned long flags;
+ time64_t time;
u32 val;
- ret = rtc_tm_to_time(&alrm->time, &time);
-
- if (ret)
- goto out;
+ time = rtc_tm_to_time64(&alrm->time);
spin_lock_irqsave(&rtc->lock, flags);
- rtc_delayed_write(time, rtc, RTC_ALARM1);
+ rtc_delayed_write((u32)time, rtc, RTC_ALARM1);
if (alrm->enabled) {
rtc_delayed_write(RTC_IRQ1_AL_EN, rtc, RTC_IRQ1_CONF);
@@ -202,8 +197,7 @@ static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
spin_unlock_irqrestore(&rtc->lock, flags);
-out:
- return ret;
+ return 0;
}
static int armada38x_rtc_alarm_irq_enable(struct device *dev,
--
2.10.2
^ permalink raw reply related
* [PATCH 3/3] rtc: armada38x: Prepare for being use on 64 bits
From: Gregory CLEMENT @ 2016-12-08 17:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208171010.29446-1-gregory.clement@free-electrons.com>
The drivers are supposed to be portable, however there are few assumption
done here about the unsigned long size. Make sure we use the accurate
width for the variable.
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
drivers/rtc/rtc-armada38x.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c
index b8a74ffaae80..c4138130febf 100644
--- a/drivers/rtc/rtc-armada38x.c
+++ b/drivers/rtc/rtc-armada38x.c
@@ -84,14 +84,14 @@ static void rtc_update_mbus_timing_params(struct armada38x_rtc *rtc)
}
struct str_value_to_freq {
- unsigned long value;
+ u32 value;
u8 freq;
} __packed;
-static unsigned long read_rtc_register_wa(struct armada38x_rtc *rtc, u8 rtc_reg)
+static u32 read_rtc_register_wa(struct armada38x_rtc *rtc, u8 rtc_reg)
{
- unsigned long value_array[SAMPLE_NR], i, j, value;
- unsigned long max = 0, index_max = SAMPLE_NR - 1;
+ int i, j, index_max = SAMPLE_NR - 1;
+ u32 value_array[SAMPLE_NR], value, max = 0;
struct str_value_to_freq value_to_freq[SAMPLE_NR];
for (i = 0; i < SAMPLE_NR; i++) {
--
2.10.2
^ permalink raw reply related
* [PATCH 1/3] rtc: armada38x: improve RTC errata implementation
From: Andrew Lunn @ 2016-12-08 17:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208171010.29446-2-gregory.clement@free-electrons.com>
> +struct str_value_to_freq {
> + unsigned long value;
> + u8 freq;
> +} __packed;
> +
> +static unsigned long read_rtc_register_wa(struct armada38x_rtc *rtc, u8 rtc_reg)
> +{
> + unsigned long value_array[SAMPLE_NR], i, j, value;
> + unsigned long max = 0, index_max = SAMPLE_NR - 1;
> + struct str_value_to_freq value_to_freq[SAMPLE_NR];
Hi Gregory
This appears to be putting over 900 bytes on the stack. Is there any
danger of overflowing the stack? Would it be safer to make these
arrays part of armada38x_rtc?
Andrew
^ permalink raw reply
* [PATCH 5/8] efi: Get the secure boot status [ver #5]
From: David Howells @ 2016-12-08 17:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208124236.GA8757@wunner.de>
Lukas Wunner <lukas@wunner.de> wrote:
> > +out_efi_err:
> > + pr_efi_err(sys_table_arg, "Could not determine UEFI Secure Boot status.\n");
> > + if (status == EFI_NOT_FOUND)
> > + return efi_secureboot_mode_disabled;
> > + return efi_secureboot_mode_unknown;
> > +}
>
> In the out_efi_err path, the if-statement needs to come before the
> pr_efi_err() call. Otherwise it would be a change of behaviour for
> ARM to what we have now.
As I understand it, if the BIOS is an EFI BIOS, these variables must exist -
in which case I would argue that the pr_efi_err-statement should be before the
if-statement.
David
^ permalink raw reply
* [PATCH v18 00/15] acpi, clocksource: add GTDT driver and GTDT support in arm_arch_timer
From: fu.wei at linaro.org @ 2016-12-08 17:33 UTC (permalink / raw)
To: linux-arm-kernel
From: Fu Wei <fu.wei@linaro.org>
This patchset:
(1)Preparation for adding GTDT support in arm_arch_timer:
1. Move some enums and marcos to header file;
2. Add a new enum for spi type;
3. Improve printk relevant code;
4. Rename some enums and defines;
5. Rework PPI determination;
6. Rework counter frequency detection;
7. Refactor arch_timer_needs_probing, move it into DT init call
8. Introduce some new structs and refactor the MMIO timer init code
for reusing some common code.
(2)Introduce ACPI GTDT parser: drivers/acpi/arm64/acpi_gtdt.c
Parse all kinds of timer in GTDT table of ACPI:arch timer,
memory-mapped timer and SBSA Generic Watchdog timer.
This driver can help to simplify all the relevant timer drivers,
and separate all the ACPI GTDT knowledge from them.
(3)Simplify ACPI code for arm_arch_timer
(4)Add GTDT support for ARM memory-mapped timer.
This patchset has been tested on the following platforms with ACPI enabled:
(1)ARM Foundation v8 model
Changelog:
v18: https://lkml.org/lkml/2016/12/8/
Fix 8/15 patch problem of "int ret;" in arch_timer_acpi_init
Rebase to 4.9.0-rc8-g9269898
v17: https://lkml.org/lkml/2016/11/25/140
Take out some cleanups from 4/15.
Merge 5/15 and 6/15, improve PPI determination code,
improve commit message.
Rework counter frequency detection.
Move arch_timer_needs_of_probing into DT init call.
Move Platform Timer scan loop back to timer init call to avoid allocating
and free memory.
Improve all the exported functions' comment.
v16: https://lkml.org/lkml/2016/11/16/268
Fix patchset problem about static enum ppi_nr of 01/13 in v15.
Refactor arch_timer_detect_rate.
Refactor arch_timer_needs_probing.
v15: https://lkml.org/lkml/2016/11/15/366
Re-order patches
Add arm_arch_timer refactoring patches to prepare for GTDT:
1. rename some enums and defines, and some cleanups
2. separate out arch_timer_uses_ppi init code and fix a potential bug
3. Improve some new structs, refactor the timer init code.
Since the some structs have been changed, GTDT parser for memory-mapped
timer and SBSA Generic Watchdog timer have been update.
v14: https://lkml.org/lkml/2016/9/28/573
Separate memory-mapped timer GTDT support into two patches
1. Refactor the timer init code to prepare for GTDT
2. Add GTDT support for memory-mapped timer
v13: http://www.mail-archive.com/linux-kernel at vger.kernel.org/msg1231717.html
Improve arm_arch_timer code for memory-mapped
timer GTDT support, refactor original memory-mapped timer
dt support for reusing some common code.
v12: https://lkml.org/lkml/2016/9/13/250
Rebase to latest Linux 4.8-rc6
Delete the confusing "skipping" in the error message.
V11: https://lkml.org/lkml/2016/9/6/354
Rebase to latest Linux 4.8-rc5
Delete typedef (suggested by checkpatch.pl)
V10: https://lkml.org/lkml/2016/7/26/215
Drop the "readq" patch.
Rebase to latest Linux 4.7.
V9: https://lkml.org/lkml/2016/7/25/345
Improve pr_err message in acpi gtdt driver.
Update Commit message for 7/9
shorten the irq mapping function name
Improve GTDT driver for memory-mapped timer
v8: https://lkml.org/lkml/2016/7/19/660
Improve "pr_fmt(fmt)" definition: add "ACPI" in front of "GTDT",
and also improve printk message.
Simplify is_timer_block and is_watchdog.
Merge acpi_gtdt_desc_init and gtdt_arch_timer_init into acpi_gtdt_init();
Delete __init in include/linux/acpi.h for GTDT API
Make ARM64 select GTDT.
Delete "#include <linux/module.h>" from acpi_gtdt.c
Simplify GT block parse code.
v7: https://lkml.org/lkml/2016/7/13/769
Move the GTDT driver to drivers/acpi/arm64
Add add the ARM64-specific ACPI Support maintainers in MAINTAINERS
Merge 3 patches of GTDT parser driver.
Fix the for_each_platform_timer bug.
v6: https://lkml.org/lkml/2016/6/29/580
split the GTDT driver to 4 parts: basic, arch_timer, memory-mapped timer,
and SBSA Generic Watchdog timer
Improve driver by suggestions and example code from Daniel Lezcano
v5: https://lkml.org/lkml/2016/5/24/356
Sorting out all patches, simplify the API of GTDT driver:
GTDT driver just fills the data struct for arm_arch_timer driver.
v4: https://lists.linaro.org/pipermail/linaro-acpi/2016-March/006667.html
Delete the kvm relevant patches
Separate two patches for sorting out the code for arm_arch_timer.
Improve irq info export code to allow missing irq info in GTDT table.
v3: https://lkml.org/lkml/2016/2/1/658
Improve GTDT driver code:
(1)improve pr_* by defining pr_fmt(fmt)
(2)simplify gtdt_sbsa_gwdt_init
(3)improve gtdt_arch_timer_data_init, if table is NULL, it will try
to get GTDT table.
Move enum ppi_nr to arm_arch_timer.h, and add enum spi_nr.
Add arm_arch_timer get ppi from DT and GTDT support for kvm.
v2: https://lkml.org/lkml/2015/12/2/10
Rebase to latest kernel version(4.4-rc3).
Fix the bug about the config problem,
use CONFIG_ACPI_GTDT instead of CONFIG_ACPI in arm_arch_timer.c
v1: The first upstreaming version: https://lkml.org/lkml/2015/10/28/553
Fu Wei (15):
clocksource/drivers/arm_arch_timer: Move enums and defines to header
file
clocksource/drivers/arm_arch_timer: Add a new enum for spi type
clocksource/drivers/arm_arch_timer: Improve printk relevant code
clocksource/drivers/arm_arch_timer: rename some enums and defines.
clocksource/drivers/arm_arch_timer: rework PPI determination
clocksource/drivers/arm_arch_timer: Rework counter frequency
detection.
clocksource/drivers/arm_arch_timer: Refactor arch_timer_needs_probing
clocksource/drivers/arm_arch_timer: move arch_timer_needs_of_probing
into DT init call
clocksource/drivers/arm_arch_timer: Introduce some new structs to
prepare for GTDT
clocksource/drivers/arm_arch_timer: Refactor the timer init code to
prepare for GTDT
acpi/arm64: Add GTDT table parse driver
clocksource/drivers/arm_arch_timer: Simplify ACPI support code.
acpi/arm64: Add memory-mapped timer support in GTDT driver
clocksource/drivers/arm_arch_timer: Add GTDT support for memory-mapped
timer
acpi/arm64: Add SBSA Generic Watchdog support in GTDT driver
arch/arm64/Kconfig | 1 +
drivers/acpi/arm64/Kconfig | 3 +
drivers/acpi/arm64/Makefile | 1 +
drivers/acpi/arm64/gtdt.c | 374 +++++++++++++++++++++++++++
drivers/clocksource/arm_arch_timer.c | 478 ++++++++++++++++++++---------------
drivers/watchdog/Kconfig | 1 +
include/clocksource/arm_arch_timer.h | 45 +++-
include/linux/acpi.h | 7 +
virt/kvm/arm/hyp/timer-sr.c | 6 +-
9 files changed, 708 insertions(+), 208 deletions(-)
create mode 100644 drivers/acpi/arm64/gtdt.c
--
2.9.3
^ permalink raw reply
* [PATCH v18 01/15] clocksource/drivers/arm_arch_timer: Move enums and defines to header file
From: fu.wei at linaro.org @ 2016-12-08 17:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208173319.6618-1-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
To support the arm_arch_timer via ACPI we need to share defines and enums
between the driver and the ACPI parser code.
Split out the relevant defines and enums into arm_arch_timer.h, and
change "enum ppi_nr" to "enum arch_timer_ppi_nr" to avoid the potential
name clashes.
Also switch "enum ppi_nr" to "enum arch_timer_ppi_nr" in
arm_arch_timer.c.
No functional change.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Tested-by: Xiongfeng Wang <wangxiongfeng2@huawei.com>
---
drivers/clocksource/arm_arch_timer.c | 13 +------------
include/clocksource/arm_arch_timer.h | 12 ++++++++++++
2 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 73c487d..21068be 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -51,8 +51,6 @@
#define CNTV_TVAL 0x38
#define CNTV_CTL 0x3c
-#define ARCH_CP15_TIMER BIT(0)
-#define ARCH_MEM_TIMER BIT(1)
static unsigned arch_timers_present __initdata;
static void __iomem *arch_counter_base;
@@ -65,20 +63,11 @@ struct arch_timer {
#define to_arch_timer(e) container_of(e, struct arch_timer, evt)
static u32 arch_timer_rate;
-
-enum ppi_nr {
- PHYS_SECURE_PPI,
- PHYS_NONSECURE_PPI,
- VIRT_PPI,
- HYP_PPI,
- MAX_TIMER_PPI
-};
-
static int arch_timer_ppi[MAX_TIMER_PPI];
static struct clock_event_device __percpu *arch_timer_evt;
-static enum ppi_nr arch_timer_uses_ppi = VIRT_PPI;
+static enum arch_timer_ppi_nr arch_timer_uses_ppi = VIRT_PPI;
static bool arch_timer_c3stop;
static bool arch_timer_mem_use_virtual;
diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h
index caedb74..557f869 100644
--- a/include/clocksource/arm_arch_timer.h
+++ b/include/clocksource/arm_arch_timer.h
@@ -16,9 +16,13 @@
#ifndef __CLKSOURCE_ARM_ARCH_TIMER_H
#define __CLKSOURCE_ARM_ARCH_TIMER_H
+#include <linux/bitops.h>
#include <linux/timecounter.h>
#include <linux/types.h>
+#define ARCH_CP15_TIMER BIT(0)
+#define ARCH_MEM_TIMER BIT(1)
+
#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
#define ARCH_TIMER_CTRL_IT_STAT (1 << 2)
@@ -34,6 +38,14 @@ enum arch_timer_reg {
ARCH_TIMER_REG_TVAL,
};
+enum arch_timer_ppi_nr {
+ PHYS_SECURE_PPI,
+ PHYS_NONSECURE_PPI,
+ VIRT_PPI,
+ HYP_PPI,
+ MAX_TIMER_PPI
+};
+
#define ARCH_TIMER_PHYS_ACCESS 0
#define ARCH_TIMER_VIRT_ACCESS 1
#define ARCH_TIMER_MEM_PHYS_ACCESS 2
--
2.9.3
^ permalink raw reply related
* [PATCH v18 02/15] clocksource/drivers/arm_arch_timer: Add a new enum for spi type
From: fu.wei at linaro.org @ 2016-12-08 17:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208173319.6618-1-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
This patch add a new enum "arch_timer_spi_nr" and use it in the driver.
Just for code's readability, no functional change.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
---
drivers/clocksource/arm_arch_timer.c | 4 ++--
include/clocksource/arm_arch_timer.h | 6 ++++++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 21068be..63bb532 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -960,9 +960,9 @@ static int __init arch_timer_mem_init(struct device_node *np)
}
if (arch_timer_mem_use_virtual)
- irq = irq_of_parse_and_map(best_frame, 1);
+ irq = irq_of_parse_and_map(best_frame, ARCH_TIMER_VIRT_SPI);
else
- irq = irq_of_parse_and_map(best_frame, 0);
+ irq = irq_of_parse_and_map(best_frame, ARCH_TIMER_PHYS_SPI);
ret = -EINVAL;
if (!irq) {
diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h
index 557f869..d23c381 100644
--- a/include/clocksource/arm_arch_timer.h
+++ b/include/clocksource/arm_arch_timer.h
@@ -46,6 +46,12 @@ enum arch_timer_ppi_nr {
MAX_TIMER_PPI
};
+enum arch_timer_spi_nr {
+ ARCH_TIMER_PHYS_SPI,
+ ARCH_TIMER_VIRT_SPI,
+ ARCH_TIMER_MAX_TIMER_SPI
+};
+
#define ARCH_TIMER_PHYS_ACCESS 0
#define ARCH_TIMER_VIRT_ACCESS 1
#define ARCH_TIMER_MEM_PHYS_ACCESS 2
--
2.9.3
^ permalink raw reply related
* [PATCH v18 03/15] clocksource/drivers/arm_arch_timer: Improve printk relevant code
From: fu.wei at linaro.org @ 2016-12-08 17:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208173319.6618-1-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
This patch defines pr_fmt(fmt) for all pr_* functions,
then the pr_* doesn't need to add "arch_timer:" everytime.
According to the suggestion from checkpatch.pl:
(1) delete some Blank Spaces in arch_timer_banner;
(2) delete a redundant Tab in a bland line of arch_timer_init(void)
No functional change.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Tested-by: Xiongfeng Wang <wangxiongfeng2@huawei.com>
---
drivers/clocksource/arm_arch_timer.c | 49 ++++++++++++++++++------------------
1 file changed, 25 insertions(+), 24 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 63bb532..15341cf 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -32,6 +32,9 @@
#include <clocksource/arm_arch_timer.h>
+#undef pr_fmt
+#define pr_fmt(fmt) "arch_timer: " fmt
+
#define CNTTIDR 0x08
#define CNTTIDR_VIRT(n) (BIT(1) << ((n) * 4))
@@ -504,22 +507,22 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np)
/* Check the timer frequency. */
if (arch_timer_rate == 0)
- pr_warn("Architected timer frequency not available\n");
+ pr_warn("frequency not available\n");
}
static void arch_timer_banner(unsigned type)
{
- pr_info("Architected %s%s%s timer(s) running at %lu.%02luMHz (%s%s%s).\n",
- type & ARCH_CP15_TIMER ? "cp15" : "",
- type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? " and " : "",
- type & ARCH_MEM_TIMER ? "mmio" : "",
- (unsigned long)arch_timer_rate / 1000000,
- (unsigned long)(arch_timer_rate / 10000) % 100,
- type & ARCH_CP15_TIMER ?
- (arch_timer_uses_ppi == VIRT_PPI) ? "virt" : "phys" :
+ pr_info("%s%s%s timer(s) running@%lu.%02luMHz (%s%s%s).\n",
+ type & ARCH_CP15_TIMER ? "cp15" : "",
+ type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? " and " : "",
+ type & ARCH_MEM_TIMER ? "mmio" : "",
+ (unsigned long)arch_timer_rate / 1000000,
+ (unsigned long)(arch_timer_rate / 10000) % 100,
+ type & ARCH_CP15_TIMER ?
+ (arch_timer_uses_ppi == VIRT_PPI) ? "virt" : "phys" :
"",
- type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? "/" : "",
- type & ARCH_MEM_TIMER ?
+ type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? "/" : "",
+ type & ARCH_MEM_TIMER ?
arch_timer_mem_use_virtual ? "virt" : "phys" :
"");
}
@@ -618,8 +621,7 @@ static void __init arch_counter_register(unsigned type)
static void arch_timer_stop(struct clock_event_device *clk)
{
- pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n",
- clk->irq, smp_processor_id());
+ pr_debug("disable IRQ%d cpu #%d\n", clk->irq, smp_processor_id());
disable_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi]);
if (arch_timer_has_nonsecure_ppi())
@@ -712,8 +714,7 @@ static int __init arch_timer_register(void)
}
if (err) {
- pr_err("arch_timer: can't register interrupt %d (%d)\n",
- ppi, err);
+ pr_err("can't register interrupt %d (%d)\n", ppi, err);
goto out_free;
}
@@ -766,7 +767,7 @@ static int __init arch_timer_mem_register(void __iomem *base, unsigned int irq)
ret = request_irq(irq, func, IRQF_TIMER, "arch_mem_timer", &t->evt);
if (ret) {
- pr_err("arch_timer: Failed to request mem timer irq\n");
+ pr_err("Failed to request mem timer irq\n");
kfree(t);
}
@@ -844,7 +845,7 @@ static int __init arch_timer_init(void)
}
if (!has_ppi) {
- pr_warn("arch_timer: No interrupt available, giving up\n");
+ pr_warn("No interrupt available, giving up\n");
return -EINVAL;
}
}
@@ -858,7 +859,7 @@ static int __init arch_timer_init(void)
return ret;
arch_timer_kvm_info.virtual_irq = arch_timer_ppi[VIRT_PPI];
-
+
return 0;
}
@@ -867,7 +868,7 @@ static int __init arch_timer_of_init(struct device_node *np)
int i;
if (arch_timers_present & ARCH_CP15_TIMER) {
- pr_warn("arch_timer: multiple nodes in dt, skipping\n");
+ pr_warn("multiple nodes in dt, skipping\n");
return 0;
}
@@ -911,7 +912,7 @@ static int __init arch_timer_mem_init(struct device_node *np)
arch_timers_present |= ARCH_MEM_TIMER;
cntctlbase = of_iomap(np, 0);
if (!cntctlbase) {
- pr_err("arch_timer: Can't find CNTCTLBase\n");
+ pr_err("Can't find CNTCTLBase\n");
return -ENXIO;
}
@@ -926,7 +927,7 @@ static int __init arch_timer_mem_init(struct device_node *np)
u32 cntacr;
if (of_property_read_u32(frame, "frame-number", &n)) {
- pr_err("arch_timer: Missing frame-number\n");
+ pr_err("Missing frame-number\n");
of_node_put(frame);
goto out;
}
@@ -955,7 +956,7 @@ static int __init arch_timer_mem_init(struct device_node *np)
ret= -ENXIO;
base = arch_counter_base = of_iomap(best_frame, 0);
if (!base) {
- pr_err("arch_timer: Can't map frame's registers\n");
+ pr_err("Can't map frame's registers\n");
goto out;
}
@@ -966,7 +967,7 @@ static int __init arch_timer_mem_init(struct device_node *np)
ret = -EINVAL;
if (!irq) {
- pr_err("arch_timer: Frame missing %s irq",
+ pr_err("Frame missing %s irq",
arch_timer_mem_use_virtual ? "virt" : "phys");
goto out;
}
@@ -1008,7 +1009,7 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
struct acpi_table_gtdt *gtdt;
if (arch_timers_present & ARCH_CP15_TIMER) {
- pr_warn("arch_timer: already initialized, skipping\n");
+ pr_warn("already initialized, skipping\n");
return -EINVAL;
}
--
2.9.3
^ permalink raw reply related
* [PATCH v18 04/15] clocksource/drivers/arm_arch_timer: rename some enums and defines.
From: fu.wei at linaro.org @ 2016-12-08 17:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208173319.6618-1-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
Rename some enums and defines, to unify the format of enums and defines
in arm_arch_timer.h, also update all the users of these enums and defines:
drivers/clocksource/arm_arch_timer.c
virt/kvm/arm/hyp/timer-sr.c
No functional change.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Tested-by: Xiongfeng Wang <wangxiongfeng2@huawei.com>
---
drivers/clocksource/arm_arch_timer.c | 111 ++++++++++++++++++-----------------
include/clocksource/arm_arch_timer.h | 24 ++++----
virt/kvm/arm/hyp/timer-sr.c | 6 +-
3 files changed, 73 insertions(+), 68 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 15341cf..231175b 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -66,11 +66,11 @@ struct arch_timer {
#define to_arch_timer(e) container_of(e, struct arch_timer, evt)
static u32 arch_timer_rate;
-static int arch_timer_ppi[MAX_TIMER_PPI];
+static int arch_timer_ppi[ARCH_TIMER_MAX_TIMER_PPI];
static struct clock_event_device __percpu *arch_timer_evt;
-static enum arch_timer_ppi_nr arch_timer_uses_ppi = VIRT_PPI;
+static enum arch_timer_ppi_nr arch_timer_uses_ppi = ARCH_TIMER_VIRT_PPI;
static bool arch_timer_c3stop;
static bool arch_timer_mem_use_virtual;
@@ -340,7 +340,7 @@ static void fsl_a008585_set_sne(struct clock_event_device *clk)
if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return;
- if (arch_timer_uses_ppi == VIRT_PPI)
+ if (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI)
clk->set_next_event = fsl_a008585_set_next_event_virt;
else
clk->set_next_event = fsl_a008585_set_next_event_phys;
@@ -352,7 +352,7 @@ static void __arch_timer_setup(unsigned type,
{
clk->features = CLOCK_EVT_FEAT_ONESHOT;
- if (type == ARCH_CP15_TIMER) {
+ if (type == ARCH_TIMER_TYPE_CP15) {
if (arch_timer_c3stop)
clk->features |= CLOCK_EVT_FEAT_C3STOP;
clk->name = "arch_sys_timer";
@@ -360,14 +360,14 @@ static void __arch_timer_setup(unsigned type,
clk->cpumask = cpumask_of(smp_processor_id());
clk->irq = arch_timer_ppi[arch_timer_uses_ppi];
switch (arch_timer_uses_ppi) {
- case VIRT_PPI:
+ case ARCH_TIMER_VIRT_PPI:
clk->set_state_shutdown = arch_timer_shutdown_virt;
clk->set_state_oneshot_stopped = arch_timer_shutdown_virt;
clk->set_next_event = arch_timer_set_next_event_virt;
break;
- case PHYS_SECURE_PPI:
- case PHYS_NONSECURE_PPI:
- case HYP_PPI:
+ case ARCH_TIMER_PHYS_SECURE_PPI:
+ case ARCH_TIMER_PHYS_NONSECURE_PPI:
+ case ARCH_TIMER_HYP_PPI:
clk->set_state_shutdown = arch_timer_shutdown_phys;
clk->set_state_oneshot_stopped = arch_timer_shutdown_phys;
clk->set_next_event = arch_timer_set_next_event_phys;
@@ -447,8 +447,8 @@ static void arch_counter_set_user_access(void)
static bool arch_timer_has_nonsecure_ppi(void)
{
- return (arch_timer_uses_ppi == PHYS_SECURE_PPI &&
- arch_timer_ppi[PHYS_NONSECURE_PPI]);
+ return (arch_timer_uses_ppi == ARCH_TIMER_PHYS_SECURE_PPI &&
+ arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
}
static u32 check_ppi_trigger(int irq)
@@ -469,14 +469,15 @@ static int arch_timer_starting_cpu(unsigned int cpu)
struct clock_event_device *clk = this_cpu_ptr(arch_timer_evt);
u32 flags;
- __arch_timer_setup(ARCH_CP15_TIMER, clk);
+ __arch_timer_setup(ARCH_TIMER_TYPE_CP15, clk);
flags = check_ppi_trigger(arch_timer_ppi[arch_timer_uses_ppi]);
enable_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi], flags);
if (arch_timer_has_nonsecure_ppi()) {
- flags = check_ppi_trigger(arch_timer_ppi[PHYS_NONSECURE_PPI]);
- enable_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI], flags);
+ flags = check_ppi_trigger(arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
+ enable_percpu_irq(arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI],
+ flags);
}
arch_counter_set_user_access();
@@ -513,16 +514,17 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np)
static void arch_timer_banner(unsigned type)
{
pr_info("%s%s%s timer(s) running at %lu.%02luMHz (%s%s%s).\n",
- type & ARCH_CP15_TIMER ? "cp15" : "",
- type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? " and " : "",
- type & ARCH_MEM_TIMER ? "mmio" : "",
+ type & ARCH_TIMER_TYPE_CP15 ? "cp15" : "",
+ type == (ARCH_TIMER_TYPE_CP15 | ARCH_TIMER_TYPE_MEM) ?
+ " and " : "",
+ type & ARCH_TIMER_TYPE_MEM ? "mmio" : "",
(unsigned long)arch_timer_rate / 1000000,
(unsigned long)(arch_timer_rate / 10000) % 100,
- type & ARCH_CP15_TIMER ?
- (arch_timer_uses_ppi == VIRT_PPI) ? "virt" : "phys" :
+ type & ARCH_TIMER_TYPE_CP15 ?
+ (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) ? "virt" : "phys" :
"",
- type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? "/" : "",
- type & ARCH_MEM_TIMER ?
+ type == (ARCH_TIMER_TYPE_CP15 | ARCH_TIMER_TYPE_MEM) ? "/" : "",
+ type & ARCH_TIMER_TYPE_MEM ?
arch_timer_mem_use_virtual ? "virt" : "phys" :
"");
}
@@ -588,8 +590,9 @@ static void __init arch_counter_register(unsigned type)
u64 start_count;
/* Register the CP15 based counter if we have one */
- if (type & ARCH_CP15_TIMER) {
- if (IS_ENABLED(CONFIG_ARM64) || arch_timer_uses_ppi == VIRT_PPI)
+ if (type & ARCH_TIMER_TYPE_CP15) {
+ if (IS_ENABLED(CONFIG_ARM64) ||
+ arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI)
arch_timer_read_counter = arch_counter_get_cntvct;
else
arch_timer_read_counter = arch_counter_get_cntpct;
@@ -625,7 +628,7 @@ static void arch_timer_stop(struct clock_event_device *clk)
disable_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi]);
if (arch_timer_has_nonsecure_ppi())
- disable_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI]);
+ disable_percpu_irq(arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
clk->set_state_shutdown(clk);
}
@@ -688,24 +691,24 @@ static int __init arch_timer_register(void)
ppi = arch_timer_ppi[arch_timer_uses_ppi];
switch (arch_timer_uses_ppi) {
- case VIRT_PPI:
+ case ARCH_TIMER_VIRT_PPI:
err = request_percpu_irq(ppi, arch_timer_handler_virt,
"arch_timer", arch_timer_evt);
break;
- case PHYS_SECURE_PPI:
- case PHYS_NONSECURE_PPI:
+ case ARCH_TIMER_PHYS_SECURE_PPI:
+ case ARCH_TIMER_PHYS_NONSECURE_PPI:
err = request_percpu_irq(ppi, arch_timer_handler_phys,
"arch_timer", arch_timer_evt);
- if (!err && arch_timer_ppi[PHYS_NONSECURE_PPI]) {
- ppi = arch_timer_ppi[PHYS_NONSECURE_PPI];
+ if (!err && arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]) {
+ ppi = arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI];
err = request_percpu_irq(ppi, arch_timer_handler_phys,
"arch_timer", arch_timer_evt);
if (err)
- free_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI],
+ free_percpu_irq(arch_timer_ppi[ARCH_TIMER_PHYS_SECURE_PPI],
arch_timer_evt);
}
break;
- case HYP_PPI:
+ case ARCH_TIMER_HYP_PPI:
err = request_percpu_irq(ppi, arch_timer_handler_phys,
"arch_timer", arch_timer_evt);
break;
@@ -737,7 +740,7 @@ static int __init arch_timer_register(void)
out_unreg_notify:
free_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi], arch_timer_evt);
if (arch_timer_has_nonsecure_ppi())
- free_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI],
+ free_percpu_irq(arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI],
arch_timer_evt);
out_free:
@@ -758,7 +761,7 @@ static int __init arch_timer_mem_register(void __iomem *base, unsigned int irq)
t->base = base;
t->evt.irq = irq;
- __arch_timer_setup(ARCH_MEM_TIMER, &t->evt);
+ __arch_timer_setup(ARCH_TIMER_TYPE_MEM, &t->evt);
if (arch_timer_mem_use_virtual)
func = arch_timer_handler_virt_mem;
@@ -801,13 +804,15 @@ arch_timer_needs_probing(int type, const struct of_device_id *matches)
static int __init arch_timer_common_init(void)
{
- unsigned mask = ARCH_CP15_TIMER | ARCH_MEM_TIMER;
+ unsigned mask = ARCH_TIMER_TYPE_CP15 | ARCH_TIMER_TYPE_MEM;
/* Wait until both nodes are probed if we have two timers */
if ((arch_timers_present & mask) != mask) {
- if (arch_timer_needs_probing(ARCH_MEM_TIMER, arch_timer_mem_of_match))
+ if (arch_timer_needs_probing(ARCH_TIMER_TYPE_MEM,
+ arch_timer_mem_of_match))
return 0;
- if (arch_timer_needs_probing(ARCH_CP15_TIMER, arch_timer_of_match))
+ if (arch_timer_needs_probing(ARCH_TIMER_TYPE_CP15,
+ arch_timer_of_match))
return 0;
}
@@ -832,16 +837,16 @@ static int __init arch_timer_init(void)
* their CNTHP_*_EL2 counterparts, and use a different PPI
* number.
*/
- if (is_hyp_mode_available() || !arch_timer_ppi[VIRT_PPI]) {
+ if (is_hyp_mode_available() || !arch_timer_ppi[ARCH_TIMER_VIRT_PPI]) {
bool has_ppi;
if (is_kernel_in_hyp_mode()) {
- arch_timer_uses_ppi = HYP_PPI;
- has_ppi = !!arch_timer_ppi[HYP_PPI];
+ arch_timer_uses_ppi = ARCH_TIMER_HYP_PPI;
+ has_ppi = !!arch_timer_ppi[ARCH_TIMER_HYP_PPI];
} else {
- arch_timer_uses_ppi = PHYS_SECURE_PPI;
- has_ppi = (!!arch_timer_ppi[PHYS_SECURE_PPI] ||
- !!arch_timer_ppi[PHYS_NONSECURE_PPI]);
+ arch_timer_uses_ppi = ARCH_TIMER_PHYS_SECURE_PPI;
+ has_ppi = (!!arch_timer_ppi[ARCH_TIMER_PHYS_SECURE_PPI] ||
+ !!arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
}
if (!has_ppi) {
@@ -858,7 +863,7 @@ static int __init arch_timer_init(void)
if (ret)
return ret;
- arch_timer_kvm_info.virtual_irq = arch_timer_ppi[VIRT_PPI];
+ arch_timer_kvm_info.virtual_irq = arch_timer_ppi[ARCH_TIMER_VIRT_PPI];
return 0;
}
@@ -867,13 +872,13 @@ static int __init arch_timer_of_init(struct device_node *np)
{
int i;
- if (arch_timers_present & ARCH_CP15_TIMER) {
+ if (arch_timers_present & ARCH_TIMER_TYPE_CP15) {
pr_warn("multiple nodes in dt, skipping\n");
return 0;
}
- arch_timers_present |= ARCH_CP15_TIMER;
- for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++)
+ arch_timers_present |= ARCH_TIMER_TYPE_CP15;
+ for (i = ARCH_TIMER_PHYS_SECURE_PPI; i < ARCH_TIMER_MAX_TIMER_PPI; i++)
arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
arch_timer_detect_rate(NULL, np);
@@ -895,7 +900,7 @@ static int __init arch_timer_of_init(struct device_node *np)
*/
if (IS_ENABLED(CONFIG_ARM) &&
of_property_read_bool(np, "arm,cpu-registers-not-fw-configured"))
- arch_timer_uses_ppi = PHYS_SECURE_PPI;
+ arch_timer_uses_ppi = ARCH_TIMER_PHYS_SECURE_PPI;
return arch_timer_init();
}
@@ -909,7 +914,7 @@ static int __init arch_timer_mem_init(struct device_node *np)
unsigned int irq, ret = -EINVAL;
u32 cnttidr;
- arch_timers_present |= ARCH_MEM_TIMER;
+ arch_timers_present |= ARCH_TIMER_TYPE_MEM;
cntctlbase = of_iomap(np, 0);
if (!cntctlbase) {
pr_err("Can't find CNTCTLBase\n");
@@ -1008,28 +1013,28 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
{
struct acpi_table_gtdt *gtdt;
- if (arch_timers_present & ARCH_CP15_TIMER) {
+ if (arch_timers_present & ARCH_TIMER_TYPE_CP15) {
pr_warn("already initialized, skipping\n");
return -EINVAL;
}
gtdt = container_of(table, struct acpi_table_gtdt, header);
- arch_timers_present |= ARCH_CP15_TIMER;
+ arch_timers_present |= ARCH_TIMER_TYPE_CP15;
- arch_timer_ppi[PHYS_SECURE_PPI] =
+ arch_timer_ppi[ARCH_TIMER_PHYS_SECURE_PPI] =
map_generic_timer_interrupt(gtdt->secure_el1_interrupt,
gtdt->secure_el1_flags);
- arch_timer_ppi[PHYS_NONSECURE_PPI] =
+ arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI] =
map_generic_timer_interrupt(gtdt->non_secure_el1_interrupt,
gtdt->non_secure_el1_flags);
- arch_timer_ppi[VIRT_PPI] =
+ arch_timer_ppi[ARCH_TIMER_VIRT_PPI] =
map_generic_timer_interrupt(gtdt->virtual_timer_interrupt,
gtdt->virtual_timer_flags);
- arch_timer_ppi[HYP_PPI] =
+ arch_timer_ppi[ARCH_TIMER_HYP_PPI] =
map_generic_timer_interrupt(gtdt->non_secure_el2_interrupt,
gtdt->non_secure_el2_flags);
diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h
index d23c381..48376a5 100644
--- a/include/clocksource/arm_arch_timer.h
+++ b/include/clocksource/arm_arch_timer.h
@@ -20,18 +20,18 @@
#include <linux/timecounter.h>
#include <linux/types.h>
-#define ARCH_CP15_TIMER BIT(0)
-#define ARCH_MEM_TIMER BIT(1)
+#define ARCH_TIMER_TYPE_CP15 BIT(0)
+#define ARCH_TIMER_TYPE_MEM BIT(1)
#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
#define ARCH_TIMER_CTRL_IT_STAT (1 << 2)
-#define CNTHCTL_EL1PCTEN (1 << 0)
-#define CNTHCTL_EL1PCEN (1 << 1)
-#define CNTHCTL_EVNTEN (1 << 2)
-#define CNTHCTL_EVNTDIR (1 << 3)
-#define CNTHCTL_EVNTI (0xF << 4)
+#define ARCH_TIMER_CNTHCTL_EL1PCTEN (1 << 0)
+#define ARCH_TIMER_CNTHCTL_EL1PCEN (1 << 1)
+#define ARCH_TIMER_CNTHCTL_EVNTEN (1 << 2)
+#define ARCH_TIMER_CNTHCTL_EVNTDIR (1 << 3)
+#define ARCH_TIMER_CNTHCTL_EVNTI (0xF << 4)
enum arch_timer_reg {
ARCH_TIMER_REG_CTRL,
@@ -39,11 +39,11 @@ enum arch_timer_reg {
};
enum arch_timer_ppi_nr {
- PHYS_SECURE_PPI,
- PHYS_NONSECURE_PPI,
- VIRT_PPI,
- HYP_PPI,
- MAX_TIMER_PPI
+ ARCH_TIMER_PHYS_SECURE_PPI,
+ ARCH_TIMER_PHYS_NONSECURE_PPI,
+ ARCH_TIMER_VIRT_PPI,
+ ARCH_TIMER_HYP_PPI,
+ ARCH_TIMER_MAX_TIMER_PPI
};
enum arch_timer_spi_nr {
diff --git a/virt/kvm/arm/hyp/timer-sr.c b/virt/kvm/arm/hyp/timer-sr.c
index 798866a..695b9d9 100644
--- a/virt/kvm/arm/hyp/timer-sr.c
+++ b/virt/kvm/arm/hyp/timer-sr.c
@@ -37,7 +37,7 @@ void __hyp_text __timer_save_state(struct kvm_vcpu *vcpu)
/* Allow physical timer/counter access for the host */
val = read_sysreg(cnthctl_el2);
- val |= CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN;
+ val |= ARCH_TIMER_CNTHCTL_EL1PCTEN | ARCH_TIMER_CNTHCTL_EL1PCEN;
write_sysreg(val, cnthctl_el2);
/* Clear cntvoff for the host */
@@ -55,8 +55,8 @@ void __hyp_text __timer_restore_state(struct kvm_vcpu *vcpu)
* Physical counter access is allowed
*/
val = read_sysreg(cnthctl_el2);
- val &= ~CNTHCTL_EL1PCEN;
- val |= CNTHCTL_EL1PCTEN;
+ val &= ~ARCH_TIMER_CNTHCTL_EL1PCEN;
+ val |= ARCH_TIMER_CNTHCTL_EL1PCTEN;
write_sysreg(val, cnthctl_el2);
if (timer->enabled) {
--
2.9.3
^ permalink raw reply related
* [PATCH v18 05/15] clocksource/drivers/arm_arch_timer: rework PPI determination
From: fu.wei at linaro.org @ 2016-12-08 17:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161208173319.6618-1-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
Currently, the arch timer driver uses ARCH_TIMER_PHYS_SECURE_PPI to
mean the driver will use the secure PPI *and* potentialy also use the
non-secure PPI. This is somewhat confusing.
For arm64, where it never makes sense to use the secure PPI, this
means we must always request the useless secure PPI, adding to the
confusion. For ACPI, where we may not even have a valid secure PPI
number, this is additionally problematic. We need the driver to be
able to use *only* the non-secure PPI.
The logic to choose which PPI to use is intertwined with other logic
in arch_timer_init(). This patch factors the PPI determination out
into a new function named arch_timer_select_ppi, and then reworks it
so that we can handle having only a non-secure PPI.
This patch also moves arch_timer_ppi verification out to caller,
because we can verify the configuration from device-tree for ARM by this
way.
Meanwhile, because we will select ARCH_TIMER_PHYS_NONSECURE_PPI for ARM64,
the logic in arch_timer_register also need to be updated.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Tested-by: Xiongfeng Wang <wangxiongfeng2@huawei.com>
---
drivers/clocksource/arm_arch_timer.c | 77 +++++++++++++++++++++---------------
1 file changed, 46 insertions(+), 31 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 231175b..e43be0a 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -699,7 +699,7 @@ static int __init arch_timer_register(void)
case ARCH_TIMER_PHYS_NONSECURE_PPI:
err = request_percpu_irq(ppi, arch_timer_handler_phys,
"arch_timer", arch_timer_evt);
- if (!err && arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]) {
+ if (!err && arch_timer_has_nonsecure_ppi()) {
ppi = arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI];
err = request_percpu_irq(ppi, arch_timer_handler_phys,
"arch_timer", arch_timer_evt);
@@ -821,39 +821,41 @@ static int __init arch_timer_common_init(void)
return arch_timer_arch_init();
}
-static int __init arch_timer_init(void)
+/**
+ * arch_timer_select_ppi() - Select suitable PPI for the current system.
+ *
+ * If HYP mode is available, we know that the physical timer
+ * has been configured to be accessible from PL1. Use it, so
+ * that a guest can use the virtual timer instead.
+ *
+ * On ARMv8.1 with VH extensions, the kernel runs in HYP. VHE
+ * accesses to CNTP_*_EL1 registers are silently redirected to
+ * their CNTHP_*_EL2 counterparts, and use a different PPI
+ * number.
+ *
+ * If no interrupt provided for virtual timer, we'll have to
+ * stick to the physical timer. It'd better be accessible...
+ * For arm64 we never use the secure interrupt.
+ *
+ * Return: a suitable PPI type for the current system.
+ */
+static enum arch_timer_ppi_nr __init arch_timer_select_ppi(void)
{
- int ret;
- /*
- * If HYP mode is available, we know that the physical timer
- * has been configured to be accessible from PL1. Use it, so
- * that a guest can use the virtual timer instead.
- *
- * If no interrupt provided for virtual timer, we'll have to
- * stick to the physical timer. It'd better be accessible...
- *
- * On ARMv8.1 with VH extensions, the kernel runs in HYP. VHE
- * accesses to CNTP_*_EL1 registers are silently redirected to
- * their CNTHP_*_EL2 counterparts, and use a different PPI
- * number.
- */
- if (is_hyp_mode_available() || !arch_timer_ppi[ARCH_TIMER_VIRT_PPI]) {
- bool has_ppi;
+ if (is_hyp_mode_available() && is_kernel_in_hyp_mode())
+ return ARCH_TIMER_HYP_PPI;
- if (is_kernel_in_hyp_mode()) {
- arch_timer_uses_ppi = ARCH_TIMER_HYP_PPI;
- has_ppi = !!arch_timer_ppi[ARCH_TIMER_HYP_PPI];
- } else {
- arch_timer_uses_ppi = ARCH_TIMER_PHYS_SECURE_PPI;
- has_ppi = (!!arch_timer_ppi[ARCH_TIMER_PHYS_SECURE_PPI] ||
- !!arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
- }
+ if (arch_timer_ppi[ARCH_TIMER_VIRT_PPI])
+ return ARCH_TIMER_VIRT_PPI;
- if (!has_ppi) {
- pr_warn("No interrupt available, giving up\n");
- return -EINVAL;
- }
- }
+ if (IS_ENABLED(CONFIG_ARM64))
+ return ARCH_TIMER_PHYS_NONSECURE_PPI;
+
+ return ARCH_TIMER_PHYS_SECURE_PPI;
+}
+
+static int __init arch_timer_init(void)
+{
+ int ret;
ret = arch_timer_register();
if (ret)
@@ -901,6 +903,13 @@ static int __init arch_timer_of_init(struct device_node *np)
if (IS_ENABLED(CONFIG_ARM) &&
of_property_read_bool(np, "arm,cpu-registers-not-fw-configured"))
arch_timer_uses_ppi = ARCH_TIMER_PHYS_SECURE_PPI;
+ else
+ arch_timer_uses_ppi = arch_timer_select_ppi();
+
+ if (!arch_timer_ppi[arch_timer_uses_ppi]) {
+ pr_err("No interrupt available, giving up\n");
+ return -EINVAL;
+ }
return arch_timer_init();
}
@@ -1041,6 +1050,12 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
/* Get the frequency from CNTFRQ */
arch_timer_detect_rate(NULL, NULL);
+ arch_timer_uses_ppi = arch_timer_select_ppi();
+ if (!arch_timer_ppi[arch_timer_uses_ppi]) {
+ pr_err("No interrupt available, giving up\n");
+ return -EINVAL;
+ }
+
/* Always-on capability */
arch_timer_c3stop = !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON);
--
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