* [PATCHv2 3/4] arm64: dump: Remove max_addr
From: Mark Rutland @ 2016-10-17 11:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476311522-15381-4-git-send-email-labbott@redhat.com>
On Wed, Oct 12, 2016 at 03:32:01PM -0700, Laura Abbott wrote:
>
> max_addr was added as part of struct ptdump_info but has never actually
> been used. Remove it.
>
> Signed-off-by: Laura Abbott <labbott@redhat.com>
Reviewed-by: Mark Rutland <mark.rutland@arm.com>
Tested-by: Mark Rutland <mark.rutland@arm.com>
Thanks,
Mark.
> ---
> New for v2 of the series
> ---
> arch/arm64/include/asm/ptdump.h | 1 -
> 1 file changed, 1 deletion(-)
>
> diff --git a/arch/arm64/include/asm/ptdump.h b/arch/arm64/include/asm/ptdump.h
> index 7c35689..8fc0957 100644
> --- a/arch/arm64/include/asm/ptdump.h
> +++ b/arch/arm64/include/asm/ptdump.h
> @@ -30,7 +30,6 @@ struct ptdump_info {
> struct mm_struct *mm;
> const struct addr_marker *markers;
> unsigned long base_addr;
> - unsigned long max_addr;
> };
>
> void ptdump_walk_pgd(struct seq_file *s, struct ptdump_info *info);
> --
> 2.7.4
>
^ permalink raw reply
* [PATCHv2 2/4] arm64: dump: Make the page table dumping seq_file optional
From: Mark Rutland @ 2016-10-17 11:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476311522-15381-3-git-send-email-labbott@redhat.com>
On Wed, Oct 12, 2016 at 03:32:00PM -0700, Laura Abbott wrote:
>
> The page table dumping code always assumes it will be dumping to a
> seq_file to userspace. Future code will be taking advantage of
> the page table dumping code but will not need the seq_file. Make
> the seq_file optional for these cases.
>
> Acked-by: Mark Rutland <mark.rutland@arm.com>
Now that I'm back in the office with access to hardware, I've been able
to give this a spin. FWIW, feel free to upgrade the above to:
Reviewed-by: Mark Rutland <mark.rutland@arm.com>
Tested-by: Mark Rutland <mark.rutland@arm.com>
Thanks,
Mark.
^ permalink raw reply
* [PATCH] ARM64-cpuinfo: Combine six calls for sequence output into one seq_printf() call in c_show()
From: Mark Rutland @ 2016-10-17 10:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <83d98772-8872-1b75-a9a5-5f08b8462e18@users.sourceforge.net>
On Sun, Oct 16, 2016 at 09:03:52PM +0200, SF Markus Elfring wrote:
> From: Markus Elfring <elfring@users.sourceforge.net>
> Date: Sun, 16 Oct 2016 20:48:28 +0200
>
> Some data were printed into a sequence by six separate function calls.
> Print the same data by a single function call instead.
... why?
Beyond simply having fewer function calls, is there an upside?
This makes it harder to see the relationship between the format strings
and their associated data, and makes the code longer.
Thanks,
Mark.
> Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
> ---
> arch/arm64/kernel/cpuinfo.c | 19 +++++++++++--------
> 1 file changed, 11 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
> index b3d5b3e..f22687d 100644
> --- a/arch/arm64/kernel/cpuinfo.c
> +++ b/arch/arm64/kernel/cpuinfo.c
> @@ -148,14 +148,17 @@ static int c_show(struct seq_file *m, void *v)
> if (elf_hwcap & (1 << j))
> seq_printf(m, " %s", hwcap_str[j]);
> }
> - seq_puts(m, "\n");
> -
> - seq_printf(m, "CPU implementer\t: 0x%02x\n",
> - MIDR_IMPLEMENTOR(midr));
> - seq_printf(m, "CPU architecture: 8\n");
> - seq_printf(m, "CPU variant\t: 0x%x\n", MIDR_VARIANT(midr));
> - seq_printf(m, "CPU part\t: 0x%03x\n", MIDR_PARTNUM(midr));
> - seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr));
> + seq_printf(m,
> + "\n"
> + "CPU implementer\t: 0x%02x\n"
> + "CPU architecture: 8\n"
> + "CPU variant\t: 0x%x\n"
> + "CPU part\t: 0x%03x\n"
> + "CPU revision\t: %d\n\n",
> + MIDR_IMPLEMENTOR(midr),
> + MIDR_VARIANT(midr),
> + MIDR_PARTNUM(midr),
> + MIDR_REVISION(midr));
> }
>
> return 0;
> --
> 2.10.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply
* [PATCHv2 1/4] arm64: dump: Make ptdump debugfs a separate option
From: Mark Rutland @ 2016-10-17 10:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476311522-15381-2-git-send-email-labbott@redhat.com>
Hi Laura,
In looking at this, I realised I was confused about ptdump_initialize()
previously, and now see why we can't decouple the debugfs registration
of the kernel page tables from the rest of the ptdump init. Sorry for
the noise on that.
Aside from one issue below, this looks good to me.
On Wed, Oct 12, 2016 at 03:31:59PM -0700, Laura Abbott wrote:
> diff --git a/arch/arm64/include/asm/ptdump.h b/arch/arm64/include/asm/ptdump.h
> index 07b8ed0..7c35689 100644
> --- a/arch/arm64/include/asm/ptdump.h
> +++ b/arch/arm64/include/asm/ptdump.h
> @@ -16,9 +16,10 @@
> #ifndef __ASM_PTDUMP_H
> #define __ASM_PTDUMP_H
>
> -#ifdef CONFIG_ARM64_PTDUMP
> +#ifdef CONFIG_ARM64_PTDUMP_CORE
>
> #include <linux/mm_types.h>
> +#include <linux/seq_file.h>
>
> struct addr_marker {
> unsigned long start_address;
> @@ -32,13 +33,15 @@ struct ptdump_info {
> unsigned long max_addr;
> };
>
> -int ptdump_register(struct ptdump_info *info, const char *name);
> -
> +void ptdump_walk_pgd(struct seq_file *s, struct ptdump_info *info);
> +#ifdef CONFIG_ARM64_PTDUMP_DEBUGFS
> +int ptdump_debugfs_register(struct ptdump_info *info, const char *name);
> #else
> -static inline int ptdump_register(struct ptdump_info *info, const char *name)
> +static inline int ptdump_debugfs_register(struct ptdump_info *info,
> + const char *name)
> {
> return 0;
> }
> -#endif /* CONFIG_ARM64_PTDUMP */
> +#endif
I think you didn't mean to remove the existing endif here?
It's still needed to guard the CONFIG_ARM64_PTDUMP_CORE case, and the
new one is needed for the new #ifdef CONFIG_ARM64_PTDUMP_DEBUGFS.
Without it, I get a build error with this patch atop of v4.9-rc1 with
CONFIG_ARM64_PTDUMP_DEBUGFS selected:
[mark at leverpostej:~/src/linux]% uselinaro 15.08 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j10 -s
In file included from arch/arm64/mm/ptdump_debugfs.c:4:0:
./arch/arm64/include/asm/ptdump.h:16:0: error: unterminated #ifndef
#ifndef __ASM_PTDUMP_H
^
make[1]: *** [arch/arm64/mm/ptdump_debugfs.o] Error 1
make[1]: *** Waiting for unfinished jobs....
With that #endif restored, everything works fine. So FWIW, with that:
Reviewed-by: Mark Rutland <mark.rutland@arm.com>
Tested-by: Mark Rutland <mark.rutland@arm.com>
[...]
> diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c
> index 7c75a8d..33d35e8 100644
> --- a/drivers/firmware/efi/arm-runtime.c
> +++ b/drivers/firmware/efi/arm-runtime.c
> @@ -39,7 +39,7 @@ static struct mm_struct efi_mm = {
> .mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
> };
>
> -#ifdef CONFIG_ARM64_PTDUMP
> +#ifdef CONFIG_ARM64_PTDUMP_DEBUGFS
> #include <asm/ptdump.h>
>
> static struct ptdump_info efi_ptdump_info = {
> @@ -53,10 +53,9 @@ static struct ptdump_info efi_ptdump_info = {
>
> static int __init ptdump_init(void)
> {
> - return ptdump_register(&efi_ptdump_info, "efi_page_tables");
> + return ptdump_debugfs_register(&efi_ptdump_info, "efi_page_tables");
> }
> device_initcall(ptdump_init);
> -
> #endif
For the EFI changes, we'll need an ack from Ard or Matt; this should
probably be Cc'd to the linux-efi list for that.
Thanks,
Mark.
^ permalink raw reply
* master build: 2 failures 4 warnings (v4.8-11811-g35ff96d)
From: Mark Brown @ 2016-10-17 10:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <5682869.nmeogTxQJD@wuerfel>
On Fri, Oct 14, 2016 at 10:12:04PM +0200, Arnd Bergmann wrote:
> On Friday, October 14, 2016 11:33:44 AM CEST Lee Jones wrote:
> > I'm waiting on a firm answer from Arnd and Thomas before applying
> > anything.
> I don't understand what the hardware does, so I'm not really
> able to answer this. Maybe just mark the driver as 'depends on
> BROKEN' for now?
There are a few boards using it, we should probably revert the broken
patches rather than just completely disable the driver.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161017/3b8b7d09/attachment-0001.sig>
^ permalink raw reply
* [PATCH] drivers: psci: Allow PSCI node to be disabled
From: Thierry Reding @ 2016-10-17 10:46 UTC (permalink / raw)
To: linux-arm-kernel
From: Thierry Reding <treding@nvidia.com>
Allow disabling PSCI support (mostly for testing purposes) by setting
the status property to "disabled". This makes the node behave in much
the same way as proper device nodes.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
drivers/firmware/psci.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index 8263429e21b8..6c60a5087caf 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -630,7 +630,7 @@ int __init psci_dt_init(void)
np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np);
- if (!np)
+ if (!np || !of_device_is_available(np))
return -ENODEV;
init_fn = (psci_initcall_t)matched_np->data;
--
2.10.0
^ permalink raw reply related
* [PATCH] arm64: KVM: Take S1 walks into account when determining S2 write faults
From: Marc Zyngier @ 2016-10-17 10:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017102022.GB1442@arm.com>
On 17/10/16 11:20, Will Deacon wrote:
> On Thu, Sep 29, 2016 at 09:14:32PM +0200, Christoffer Dall wrote:
>> On Thu, Sep 29, 2016 at 12:37:01PM +0100, Will Deacon wrote:
>>> The WnR bit in the HSR/ESR_EL2 indicates whether a data abort was
>>> generated by a read or a write instruction. For stage 2 data aborts
>>> generated by a stage 1 translation table walk (i.e. the actual page
>>> table access faults at EL2), the WnR bit therefore reports whether the
>>> instruction generating the walk was a load or a store, *not* whether the
>>> page table walker was reading or writing the entry.
>>>
>>> For page tables marked as read-only at stage 2 (e.g. due to KSM merging
>>> them with the tables from another guest), this could result in livelock,
>>> where a page table walk generated by a load instruction attempts to
>>> set the access flag in the stage 1 descriptor, but fails to trigger
>>> CoW in the host since only a read fault is reported.
>>>
>>> This patch modifies the arm64 kvm_vcpu_dabt_iswrite function to
>>> take into account stage 2 faults in stage 1 walks. Since DBM cannot be
>>> disabled at EL2 for CPUs that implement it, we assume that these faults
>>> are always causes by writes, avoiding the livelock situation at the
>>> expense of occasional, spurious CoWs.
>>>
>>> We could, in theory, do a bit better by checking the guest TCR
>>> configuration and inspecting the page table to see why the PTE faulted.
>>> However, I doubt this is measurable in practice, and the threat of
>>> livelock is real.
>>>
>>> Cc: Marc Zyngier <marc.zyngier@arm.com>
>>> Cc: Christoffer Dall <christoffer.dall@linaro.org>
>>> Cc: Julien Grall <julien.grall@arm.com>
>>> Signed-off-by: Will Deacon <will.deacon@arm.com>
>>
>> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
>>
>> Applied,
>
> This doesn't seem to be in 4.9-rc1. Could you please dig it up?
Looks like this patch has been lingering in -queue. I'll push it on
master as a fix for -rc2.
Thanks for the heads up.
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* [PATCH] arm64: KVM: Take S1 walks into account when determining S2 write faults
From: Will Deacon @ 2016-10-17 10:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160929191432.GA7996@cbox>
On Thu, Sep 29, 2016 at 09:14:32PM +0200, Christoffer Dall wrote:
> On Thu, Sep 29, 2016 at 12:37:01PM +0100, Will Deacon wrote:
> > The WnR bit in the HSR/ESR_EL2 indicates whether a data abort was
> > generated by a read or a write instruction. For stage 2 data aborts
> > generated by a stage 1 translation table walk (i.e. the actual page
> > table access faults at EL2), the WnR bit therefore reports whether the
> > instruction generating the walk was a load or a store, *not* whether the
> > page table walker was reading or writing the entry.
> >
> > For page tables marked as read-only at stage 2 (e.g. due to KSM merging
> > them with the tables from another guest), this could result in livelock,
> > where a page table walk generated by a load instruction attempts to
> > set the access flag in the stage 1 descriptor, but fails to trigger
> > CoW in the host since only a read fault is reported.
> >
> > This patch modifies the arm64 kvm_vcpu_dabt_iswrite function to
> > take into account stage 2 faults in stage 1 walks. Since DBM cannot be
> > disabled at EL2 for CPUs that implement it, we assume that these faults
> > are always causes by writes, avoiding the livelock situation at the
> > expense of occasional, spurious CoWs.
> >
> > We could, in theory, do a bit better by checking the guest TCR
> > configuration and inspecting the page table to see why the PTE faulted.
> > However, I doubt this is measurable in practice, and the threat of
> > livelock is real.
> >
> > Cc: Marc Zyngier <marc.zyngier@arm.com>
> > Cc: Christoffer Dall <christoffer.dall@linaro.org>
> > Cc: Julien Grall <julien.grall@arm.com>
> > Signed-off-by: Will Deacon <will.deacon@arm.com>
>
> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
>
> Applied,
This doesn't seem to be in 4.9-rc1. Could you please dig it up?
Ta,
Will
^ permalink raw reply
* [PATCH v15 4/4] CMDQ: save energy
From: HS Liao @ 2016-10-17 10:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476699117-3001-1-git-send-email-hs.liao@mediatek.com>
1. take suspend/resume into consideration
2. use clk_disable_unprepare instead of clk_disable to save more energy
when CMDQ is idle.
Signed-off-by: HS Liao <hs.liao@mediatek.com>
---
drivers/mailbox/mtk-cmdq-mailbox.c | 94 +++++++++++++++++++++++++++++++++++---
1 file changed, 88 insertions(+), 6 deletions(-)
diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c
index d086fd8..f235249 100644
--- a/drivers/mailbox/mtk-cmdq-mailbox.c
+++ b/drivers/mailbox/mtk-cmdq-mailbox.c
@@ -22,6 +22,7 @@
#include <linux/mailbox_controller.h>
#include <linux/mailbox/mtk-cmdq-mailbox.h>
#include <linux/timer.h>
+#include <linux/workqueue.h>
#define CMDQ_THR_MAX_COUNT 3 /* main, sub, general(misc) */
#define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)
@@ -75,12 +76,19 @@ struct cmdq_task {
struct cmdq_pkt *pkt; /* the packet sent from mailbox client */
};
+struct cmdq_clk_release {
+ struct cmdq *cmdq;
+ struct work_struct release_work;
+};
+
struct cmdq {
struct mbox_controller mbox;
void __iomem *base;
u32 irq;
+ struct workqueue_struct *clk_release_wq;
struct cmdq_thread thread[CMDQ_THR_MAX_COUNT];
struct clk *clock;
+ bool suspended;
};
static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
@@ -202,10 +210,13 @@ static void cmdq_task_exec(struct cmdq_pkt *pkt, struct cmdq_thread *thread)
{
struct cmdq *cmdq;
struct cmdq_task *task;
- unsigned long curr_pa, end_pa;
+ unsigned long curr_pa, end_pa, flags;
cmdq = dev_get_drvdata(thread->chan->mbox->dev);
+ /* Client should not flush new tasks if suspended. */
+ WARN_ON(cmdq->suspended);
+
task = kzalloc(sizeof(*task), GFP_ATOMIC);
task->cmdq = cmdq;
INIT_LIST_HEAD(&task->list_entry);
@@ -215,7 +226,14 @@ static void cmdq_task_exec(struct cmdq_pkt *pkt, struct cmdq_thread *thread)
task->pkt = pkt;
if (list_empty(&thread->task_busy_list)) {
- WARN_ON(clk_enable(cmdq->clock) < 0);
+ /*
+ * Unlock for clk prepare (sleeping function).
+ * This is safe since clk_prepare_enable has internal locks.
+ */
+ spin_unlock_irqrestore(&thread->chan->lock, flags);
+ WARN_ON(clk_prepare_enable(cmdq->clock) < 0);
+ spin_lock_irqsave(&thread->chan->lock, flags);
+
WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
writel(task->pa_base, thread->base + CMDQ_THR_CURR_ADDR);
@@ -297,6 +315,26 @@ static void cmdq_task_handle_error(struct cmdq_task *task)
cmdq_thread_resume(thread);
}
+static void cmdq_clk_release_work(struct work_struct *work_item)
+{
+ struct cmdq_clk_release *clk_release = container_of(work_item,
+ struct cmdq_clk_release, release_work);
+ struct cmdq *cmdq = clk_release->cmdq;
+
+ clk_disable_unprepare(cmdq->clock);
+ kfree(clk_release);
+}
+
+static void cmdq_clk_release_schedule(struct cmdq *cmdq)
+{
+ struct cmdq_clk_release *clk_release;
+
+ clk_release = kmalloc(sizeof(*clk_release), GFP_ATOMIC);
+ clk_release->cmdq = cmdq;
+ INIT_WORK(&clk_release->release_work, cmdq_clk_release_work);
+ queue_work(cmdq->clk_release_wq, &clk_release->release_work);
+}
+
static void cmdq_thread_irq_handler(struct cmdq *cmdq,
struct cmdq_thread *thread)
{
@@ -346,7 +384,7 @@ static void cmdq_thread_irq_handler(struct cmdq *cmdq,
if (list_empty(&thread->task_busy_list)) {
cmdq_thread_disable(cmdq, thread);
- clk_disable(cmdq->clock);
+ cmdq_clk_release_schedule(cmdq);
} else {
mod_timer(&thread->timeout,
jiffies + msecs_to_jiffies(CMDQ_TIMEOUT_MS));
@@ -405,16 +443,50 @@ static void cmdq_thread_handle_timeout(unsigned long data)
cmdq_thread_resume(thread);
cmdq_thread_disable(cmdq, thread);
- clk_disable(cmdq->clock);
+ cmdq_clk_release_schedule(cmdq);
spin_unlock_irqrestore(&thread->chan->lock, flags);
}
+static int cmdq_suspend(struct device *dev)
+{
+ struct cmdq *cmdq = dev_get_drvdata(dev);
+ struct cmdq_thread *thread;
+ int i;
+ bool task_running = false;
+
+ cmdq->suspended = true;
+
+ for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
+ thread = &cmdq->thread[i];
+ if (!list_empty(&thread->task_busy_list)) {
+ mod_timer(&thread->timeout, jiffies + 1);
+ task_running = true;
+ }
+ }
+
+ if (task_running) {
+ dev_warn(dev, "exist running task(s) in suspend\n");
+ schedule();
+ }
+
+ flush_workqueue(cmdq->clk_release_wq);
+ return 0;
+}
+
+static int cmdq_resume(struct device *dev)
+{
+ struct cmdq *cmdq = dev_get_drvdata(dev);
+
+ cmdq->suspended = false;
+ return 0;
+}
+
static int cmdq_remove(struct platform_device *pdev)
{
struct cmdq *cmdq = platform_get_drvdata(pdev);
+ destroy_workqueue(cmdq->clk_release_wq);
mbox_controller_unregister(&cmdq->mbox);
- clk_unprepare(cmdq->clock);
return 0;
}
@@ -530,11 +602,20 @@ static int cmdq_probe(struct platform_device *pdev)
return err;
}
+ cmdq->clk_release_wq = alloc_ordered_workqueue(
+ "%s", WQ_MEM_RECLAIM | WQ_HIGHPRI,
+ "cmdq_clk_release");
+
platform_set_drvdata(pdev, cmdq);
- WARN_ON(clk_prepare(cmdq->clock) < 0);
+
return 0;
}
+static const struct dev_pm_ops cmdq_pm_ops = {
+ .suspend = cmdq_suspend,
+ .resume = cmdq_resume,
+};
+
static const struct of_device_id cmdq_of_ids[] = {
{.compatible = "mediatek,mt8173-gce",},
{}
@@ -545,6 +626,7 @@ static int cmdq_probe(struct platform_device *pdev)
.remove = cmdq_remove,
.driver = {
.name = "mtk_cmdq",
+ .pm = &cmdq_pm_ops,
.of_match_table = cmdq_of_ids,
}
};
--
1.9.1
^ permalink raw reply related
* [PATCH v15 3/4] arm64: dts: mt8173: Add GCE node
From: HS Liao @ 2016-10-17 10:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476699117-3001-1-git-send-email-hs.liao@mediatek.com>
This patch adds the device node of the GCE hardware for CMDQ module.
Signed-off-by: HS Liao <hs.liao@mediatek.com>
---
arch/arm64/boot/dts/mediatek/mt8173.dtsi | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index 1c71e25..d50c044 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -414,6 +414,16 @@
status = "disabled";
};
+ gce: gce at 10212000 {
+ compatible = "mediatek,mt8173-gce";
+ reg = <0 0x10212000 0 0x1000>;
+ interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&infracfg CLK_INFRA_GCE>;
+ clock-names = "gce";
+
+ #mbox-cells = <2>;
+ };
+
mipi_tx0: mipi-dphy at 10215000 {
compatible = "mediatek,mt8173-mipi-tx";
reg = <0 0x10215000 0 0x1000>;
--
1.9.1
^ permalink raw reply related
* [PATCH v15 2/4] CMDQ: Mediatek CMDQ driver
From: HS Liao @ 2016-10-17 10:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476699117-3001-1-git-send-email-hs.liao@mediatek.com>
This patch is first version of Mediatek Command Queue(CMDQ) driver. The
CMDQ is used to help write registers with critical time limitation,
such as updating display configuration during the vblank. It controls
Global Command Engine (GCE) hardware to achieve this requirement.
Currently, CMDQ only supports display related hardwares, but we expect
it can be extended to other hardwares for future requirements.
Signed-off-by: HS Liao <hs.liao@mediatek.com>
Signed-off-by: CK Hu <ck.hu@mediatek.com>
---
drivers/mailbox/Kconfig | 10 +
drivers/mailbox/Makefile | 2 +
drivers/mailbox/mtk-cmdq-mailbox.c | 552 +++++++++++++++++++++++++++++++
drivers/soc/mediatek/Kconfig | 11 +
drivers/soc/mediatek/Makefile | 1 +
drivers/soc/mediatek/mtk-cmdq-helper.c | 310 +++++++++++++++++
include/linux/mailbox/mtk-cmdq-mailbox.h | 67 ++++
include/linux/soc/mediatek/mtk-cmdq.h | 182 ++++++++++
8 files changed, 1135 insertions(+)
create mode 100644 drivers/mailbox/mtk-cmdq-mailbox.c
create mode 100644 drivers/soc/mediatek/mtk-cmdq-helper.c
create mode 100644 include/linux/mailbox/mtk-cmdq-mailbox.h
create mode 100644 include/linux/soc/mediatek/mtk-cmdq.h
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 11eebfe..5a4af2d 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -143,4 +143,14 @@ config BCM_PDC_MBOX
Mailbox implementation for the Broadcom PDC ring manager,
which provides access to various offload engines on Broadcom
SoCs. Say Y here if you want to use the Broadcom PDC.
+
+config MTK_CMDQ_MBOX
+ bool "MediaTek CMDQ Mailbox Support"
+ depends on ARM64 && ( ARCH_MEDIATEK || COMPILE_TEST )
+ select MTK_INFRACFG
+ help
+ Say yes here to add support for the MediaTek Command Queue (CMDQ)
+ mailbox driver. The CMDQ is used to help read/write registers with
+ critical time limitation, such as updating display configuration
+ during the vblank.
endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index ace6fed..b904bed 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -29,3 +29,5 @@ obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o
obj-$(CONFIG_HI6220_MBOX) += hi6220-mailbox.o
obj-$(CONFIG_BCM_PDC_MBOX) += bcm-pdc-mailbox.o
+
+obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o
diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c
new file mode 100644
index 0000000..d086fd8
--- /dev/null
+++ b/drivers/mailbox/mtk-cmdq-mailbox.c
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/mailbox/mtk-cmdq-mailbox.h>
+#include <linux/timer.h>
+
+#define CMDQ_THR_MAX_COUNT 3 /* main, sub, general(misc) */
+#define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)
+#define CMDQ_TIMEOUT_MS 1000
+#define CMDQ_IRQ_MASK 0xffff
+#define CMDQ_NUM_CMD(t) (t->cmd_buf_size / CMDQ_INST_SIZE)
+
+#define CMDQ_CURR_IRQ_STATUS 0x10
+#define CMDQ_THR_SLOT_CYCLES 0x30
+
+#define CMDQ_THR_BASE 0x100
+#define CMDQ_THR_SIZE 0x80
+#define CMDQ_THR_WARM_RESET 0x00
+#define CMDQ_THR_ENABLE_TASK 0x04
+#define CMDQ_THR_SUSPEND_TASK 0x08
+#define CMDQ_THR_CURR_STATUS 0x0c
+#define CMDQ_THR_IRQ_STATUS 0x10
+#define CMDQ_THR_IRQ_ENABLE 0x14
+#define CMDQ_THR_CURR_ADDR 0x20
+#define CMDQ_THR_END_ADDR 0x24
+#define CMDQ_THR_WAIT_TOKEN 0x30
+
+#define CMDQ_THR_ENABLED 0x1
+#define CMDQ_THR_DISABLED 0x0
+#define CMDQ_THR_SUSPEND 0x1
+#define CMDQ_THR_RESUME 0x0
+#define CMDQ_THR_STATUS_SUSPENDED BIT(1)
+#define CMDQ_THR_DO_WARM_RESET BIT(0)
+#define CMDQ_THR_ACTIVE_SLOT_CYCLES 0x3200
+#define CMDQ_THR_IRQ_DONE 0x1
+#define CMDQ_THR_IRQ_ERROR 0x12
+#define CMDQ_THR_IRQ_EN (CMDQ_THR_IRQ_ERROR | CMDQ_THR_IRQ_DONE)
+#define CMDQ_THR_IS_WAITING BIT(31)
+
+#define CMDQ_JUMP_BY_OFFSET 0x10000000
+#define CMDQ_JUMP_BY_PA 0x10000001
+
+struct cmdq_thread {
+ struct mbox_chan *chan;
+ void __iomem *base;
+ struct list_head task_busy_list;
+ struct timer_list timeout;
+ bool atomic_exec;
+};
+
+struct cmdq_task {
+ struct cmdq *cmdq;
+ struct list_head list_entry;
+ dma_addr_t pa_base;
+ struct cmdq_thread *thread;
+ struct cmdq_pkt *pkt; /* the packet sent from mailbox client */
+};
+
+struct cmdq {
+ struct mbox_controller mbox;
+ void __iomem *base;
+ u32 irq;
+ struct cmdq_thread thread[CMDQ_THR_MAX_COUNT];
+ struct clk *clock;
+};
+
+static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
+{
+ u32 status;
+
+ writel(CMDQ_THR_SUSPEND, thread->base + CMDQ_THR_SUSPEND_TASK);
+
+ /* If already disabled, treat as suspended successful. */
+ if (!(readl(thread->base + CMDQ_THR_ENABLE_TASK) & CMDQ_THR_ENABLED))
+ return 0;
+
+ if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_STATUS,
+ status, status & CMDQ_THR_STATUS_SUSPENDED, 0, 10)) {
+ dev_err(cmdq->mbox.dev, "suspend GCE thread 0x%x failed\n",
+ (u32)(thread->base - cmdq->base));
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static void cmdq_thread_resume(struct cmdq_thread *thread)
+{
+ writel(CMDQ_THR_RESUME, thread->base + CMDQ_THR_SUSPEND_TASK);
+}
+
+static int cmdq_thread_reset(struct cmdq *cmdq, struct cmdq_thread *thread)
+{
+ u32 warm_reset;
+
+ writel(CMDQ_THR_DO_WARM_RESET, thread->base + CMDQ_THR_WARM_RESET);
+ if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_WARM_RESET,
+ warm_reset, !(warm_reset & CMDQ_THR_DO_WARM_RESET),
+ 0, 10)) {
+ dev_err(cmdq->mbox.dev, "reset GCE thread 0x%x failed\n",
+ (u32)(thread->base - cmdq->base));
+ return -EFAULT;
+ }
+ writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
+ return 0;
+}
+
+static void cmdq_thread_disable(struct cmdq *cmdq, struct cmdq_thread *thread)
+{
+ cmdq_thread_reset(cmdq, thread);
+ writel(CMDQ_THR_DISABLED, thread->base + CMDQ_THR_ENABLE_TASK);
+}
+
+/* notify GCE to re-fetch commands by setting GCE thread PC */
+static void cmdq_thread_invalidate_fetched_data(struct cmdq_thread *thread)
+{
+ writel(readl(thread->base + CMDQ_THR_CURR_ADDR),
+ thread->base + CMDQ_THR_CURR_ADDR);
+}
+
+static void cmdq_task_insert_into_thread(struct cmdq_task *task)
+{
+ struct device *dev = task->cmdq->mbox.dev;
+ struct cmdq_thread *thread = task->thread;
+ struct cmdq_task *prev_task = list_last_entry(
+ &thread->task_busy_list, typeof(*task), list_entry);
+ u64 *prev_task_base = prev_task->pkt->va_base;
+
+ /* let previous task jump to this task */
+ dma_sync_single_for_cpu(dev, prev_task->pa_base,
+ prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);
+ prev_task_base[CMDQ_NUM_CMD(prev_task->pkt) - 1] =
+ (u64)CMDQ_JUMP_BY_PA << 32 | task->pa_base;
+ dma_sync_single_for_device(dev, prev_task->pa_base,
+ prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);
+
+ cmdq_thread_invalidate_fetched_data(thread);
+}
+
+static bool cmdq_command_is_wfe(u64 cmd)
+{
+ u64 wfe_option = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
+ u64 wfe_op = (u64)(CMDQ_CODE_WFE << CMDQ_OP_CODE_SHIFT) << 32;
+ u64 wfe_mask = (u64)CMDQ_OP_CODE_MASK << 32 | 0xffffffff;
+
+ return ((cmd & wfe_mask) == (wfe_op | wfe_option));
+}
+
+/* we assume tasks in the same display GCE thread are waiting the same event. */
+static void cmdq_task_remove_wfe(struct cmdq_task *task)
+{
+ struct device *dev = task->cmdq->mbox.dev;
+ u64 *base = task->pkt->va_base;
+ int i;
+
+ dma_sync_single_for_cpu(dev, task->pa_base, task->pkt->cmd_buf_size,
+ DMA_TO_DEVICE);
+ for (i = 0; i < CMDQ_NUM_CMD(task->pkt); i++)
+ if (cmdq_command_is_wfe(base[i]))
+ base[i] = (u64)CMDQ_JUMP_BY_OFFSET << 32 |
+ CMDQ_JUMP_PASS;
+ dma_sync_single_for_device(dev, task->pa_base, task->pkt->cmd_buf_size,
+ DMA_TO_DEVICE);
+}
+
+static bool cmdq_thread_is_in_wfe(struct cmdq_thread *thread)
+{
+ return readl(thread->base + CMDQ_THR_WAIT_TOKEN) & CMDQ_THR_IS_WAITING;
+}
+
+static void cmdq_thread_wait_end(struct cmdq_thread *thread,
+ unsigned long end_pa)
+{
+ struct device *dev = thread->chan->mbox->dev;
+ unsigned long curr_pa;
+
+ if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_ADDR,
+ curr_pa, curr_pa == end_pa, 1, 20))
+ dev_err(dev, "GCE thread cannot run to end.\n");
+}
+
+static void cmdq_task_exec(struct cmdq_pkt *pkt, struct cmdq_thread *thread)
+{
+ struct cmdq *cmdq;
+ struct cmdq_task *task;
+ unsigned long curr_pa, end_pa;
+
+ cmdq = dev_get_drvdata(thread->chan->mbox->dev);
+
+ task = kzalloc(sizeof(*task), GFP_ATOMIC);
+ task->cmdq = cmdq;
+ INIT_LIST_HEAD(&task->list_entry);
+ task->pa_base = dma_map_single(cmdq->mbox.dev, pkt->va_base,
+ pkt->cmd_buf_size, DMA_TO_DEVICE);
+ task->thread = thread;
+ task->pkt = pkt;
+
+ if (list_empty(&thread->task_busy_list)) {
+ WARN_ON(clk_enable(cmdq->clock) < 0);
+ WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
+
+ writel(task->pa_base, thread->base + CMDQ_THR_CURR_ADDR);
+ writel(task->pa_base + pkt->cmd_buf_size,
+ thread->base + CMDQ_THR_END_ADDR);
+ writel(CMDQ_THR_IRQ_EN, thread->base + CMDQ_THR_IRQ_ENABLE);
+ writel(CMDQ_THR_ENABLED, thread->base + CMDQ_THR_ENABLE_TASK);
+
+ mod_timer(&thread->timeout,
+ jiffies + msecs_to_jiffies(CMDQ_TIMEOUT_MS));
+ } else {
+ WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
+ curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR);
+ end_pa = readl(thread->base + CMDQ_THR_END_ADDR);
+
+ /*
+ * Atomic execution should remove the following wfe, i.e. only
+ * wait event at first task, and prevent to pause when running.
+ */
+ if (thread->atomic_exec) {
+ /* GCE is executing if command is not WFE */
+ if (!cmdq_thread_is_in_wfe(thread)) {
+ cmdq_thread_resume(thread);
+ cmdq_thread_wait_end(thread, end_pa);
+ WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
+ /* set to this task directly */
+ writel(task->pa_base,
+ thread->base + CMDQ_THR_CURR_ADDR);
+ } else {
+ cmdq_task_insert_into_thread(task);
+ cmdq_task_remove_wfe(task);
+ smp_mb(); /* modify jump before enable thread */
+ }
+ } else {
+ /* check boundary */
+ if (curr_pa == end_pa - CMDQ_INST_SIZE ||
+ curr_pa == end_pa) {
+ /* set to this task directly */
+ writel(task->pa_base,
+ thread->base + CMDQ_THR_CURR_ADDR);
+ } else {
+ cmdq_task_insert_into_thread(task);
+ smp_mb(); /* modify jump before enable thread */
+ }
+ }
+ writel(task->pa_base + pkt->cmd_buf_size,
+ thread->base + CMDQ_THR_END_ADDR);
+ cmdq_thread_resume(thread);
+ }
+ list_move_tail(&task->list_entry, &thread->task_busy_list);
+}
+
+static void cmdq_task_exec_done(struct cmdq_task *task, bool err)
+{
+ struct device *dev = task->cmdq->mbox.dev;
+ struct cmdq_cb_data cmdq_cb_data;
+
+ dma_unmap_single(dev, task->pa_base, task->pkt->cmd_buf_size,
+ DMA_TO_DEVICE);
+ if (task->pkt->cb.cb) {
+ cmdq_cb_data.err = err;
+ cmdq_cb_data.data = task->pkt->cb.data;
+ task->pkt->cb.cb(cmdq_cb_data);
+ }
+ list_del(&task->list_entry);
+}
+
+static void cmdq_task_handle_error(struct cmdq_task *task)
+{
+ struct cmdq_thread *thread = task->thread;
+ struct cmdq_task *next_task;
+
+ dev_err(task->cmdq->mbox.dev, "task 0x%p error\n", task);
+ WARN_ON(cmdq_thread_suspend(task->cmdq, thread) < 0);
+ next_task = list_first_entry_or_null(&thread->task_busy_list,
+ struct cmdq_task, list_entry);
+ if (next_task)
+ writel(next_task->pa_base, thread->base + CMDQ_THR_CURR_ADDR);
+ cmdq_thread_resume(thread);
+}
+
+static void cmdq_thread_irq_handler(struct cmdq *cmdq,
+ struct cmdq_thread *thread)
+{
+ struct cmdq_task *task, *tmp, *curr_task = NULL;
+ u32 curr_pa, irq_flag, task_end_pa;
+ bool err;
+
+ irq_flag = readl(thread->base + CMDQ_THR_IRQ_STATUS);
+ writel(~irq_flag, thread->base + CMDQ_THR_IRQ_STATUS);
+
+ /*
+ * When ISR call this function, another CPU core could run
+ * "release task" right before we acquire the spin lock, and thus
+ * reset / disable this GCE thread, so we need to check the enable
+ * bit of this GCE thread.
+ */
+ if (!(readl(thread->base + CMDQ_THR_ENABLE_TASK) & CMDQ_THR_ENABLED))
+ return;
+
+ if (irq_flag & CMDQ_THR_IRQ_ERROR)
+ err = true;
+ else if (irq_flag & CMDQ_THR_IRQ_DONE)
+ err = false;
+ else
+ return;
+
+ curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR);
+
+ list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
+ list_entry) {
+ task_end_pa = task->pa_base + task->pkt->cmd_buf_size;
+ if (curr_pa >= task->pa_base && curr_pa < task_end_pa)
+ curr_task = task;
+
+ if (!curr_task || curr_pa == task_end_pa - CMDQ_INST_SIZE) {
+ cmdq_task_exec_done(task, false);
+ kfree(task);
+ } else if (err) {
+ cmdq_task_exec_done(task, true);
+ cmdq_task_handle_error(curr_task);
+ kfree(task);
+ }
+
+ if (curr_task)
+ break;
+ }
+
+ if (list_empty(&thread->task_busy_list)) {
+ cmdq_thread_disable(cmdq, thread);
+ clk_disable(cmdq->clock);
+ } else {
+ mod_timer(&thread->timeout,
+ jiffies + msecs_to_jiffies(CMDQ_TIMEOUT_MS));
+ }
+}
+
+static irqreturn_t cmdq_irq_handler(int irq, void *dev)
+{
+ struct cmdq *cmdq = dev;
+ unsigned long irq_status, flags = 0L;
+ int bit;
+
+ irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS) & CMDQ_IRQ_MASK;
+ if (!(irq_status ^ CMDQ_IRQ_MASK))
+ return IRQ_NONE;
+
+ for_each_clear_bit(bit, &irq_status, fls(CMDQ_IRQ_MASK)) {
+ struct cmdq_thread *thread = &cmdq->thread[bit];
+
+ spin_lock_irqsave(&thread->chan->lock, flags);
+ cmdq_thread_irq_handler(cmdq, thread);
+ spin_unlock_irqrestore(&thread->chan->lock, flags);
+ }
+ return IRQ_HANDLED;
+}
+
+static void cmdq_thread_handle_timeout(unsigned long data)
+{
+ struct cmdq_thread *thread = (struct cmdq_thread *)data;
+ struct cmdq *cmdq = container_of(thread->chan->mbox, struct cmdq, mbox);
+ struct cmdq_task *task, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&thread->chan->lock, flags);
+ WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
+
+ /*
+ * Although IRQ is disabled, GCE continues to execute.
+ * It may have pending IRQ before GCE thread is suspended,
+ * so check this condition again.
+ */
+ cmdq_thread_irq_handler(cmdq, thread);
+
+ if (list_empty(&thread->task_busy_list)) {
+ cmdq_thread_resume(thread);
+ spin_unlock_irqrestore(&thread->chan->lock, flags);
+ return;
+ }
+
+ dev_err(cmdq->mbox.dev, "timeout\n");
+ list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
+ list_entry) {
+ cmdq_task_exec_done(task, true);
+ kfree(task);
+ }
+
+ cmdq_thread_resume(thread);
+ cmdq_thread_disable(cmdq, thread);
+ clk_disable(cmdq->clock);
+ spin_unlock_irqrestore(&thread->chan->lock, flags);
+}
+
+static int cmdq_remove(struct platform_device *pdev)
+{
+ struct cmdq *cmdq = platform_get_drvdata(pdev);
+
+ mbox_controller_unregister(&cmdq->mbox);
+ clk_unprepare(cmdq->clock);
+ return 0;
+}
+
+static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
+{
+ cmdq_task_exec(data, chan->con_priv);
+ return 0;
+}
+
+static int cmdq_mbox_startup(struct mbox_chan *chan)
+{
+ return 0;
+}
+
+static void cmdq_mbox_shutdown(struct mbox_chan *chan)
+{
+}
+
+static bool cmdq_mbox_last_tx_done(struct mbox_chan *chan)
+{
+ return true;
+}
+
+static const struct mbox_chan_ops cmdq_mbox_chan_ops = {
+ .send_data = cmdq_mbox_send_data,
+ .startup = cmdq_mbox_startup,
+ .shutdown = cmdq_mbox_shutdown,
+ .last_tx_done = cmdq_mbox_last_tx_done,
+};
+
+static struct mbox_chan *cmdq_xlate(struct mbox_controller *mbox,
+ const struct of_phandle_args *sp)
+{
+ int ind = sp->args[0];
+ struct cmdq_thread *thread;
+
+ if (ind >= mbox->num_chans)
+ return ERR_PTR(-EINVAL);
+
+ thread = mbox->chans[ind].con_priv;
+ thread->atomic_exec = (sp->args[1] != 0);
+ thread->chan = &mbox->chans[ind];
+
+ return &mbox->chans[ind];
+}
+
+static int cmdq_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct cmdq *cmdq;
+ int err, i;
+
+ cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
+ if (!cmdq)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ cmdq->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cmdq->base)) {
+ dev_err(dev, "failed to ioremap gce\n");
+ return PTR_ERR(cmdq->base);
+ }
+
+ cmdq->irq = platform_get_irq(pdev, 0);
+ if (!cmdq->irq) {
+ dev_err(dev, "failed to get irq\n");
+ return -EINVAL;
+ }
+ err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED,
+ "mtk_cmdq", cmdq);
+ if (err < 0) {
+ dev_err(dev, "failed to register ISR (%d)\n", err);
+ return err;
+ }
+
+ dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
+ dev, cmdq->base, cmdq->irq);
+
+ cmdq->clock = devm_clk_get(dev, "gce");
+ if (IS_ERR(cmdq->clock)) {
+ dev_err(dev, "failed to get gce clk\n");
+ return PTR_ERR(cmdq->clock);
+ }
+
+ cmdq->mbox.dev = dev;
+ cmdq->mbox.chans = devm_kcalloc(dev, CMDQ_THR_MAX_COUNT,
+ sizeof(*cmdq->mbox.chans), GFP_KERNEL);
+ if (!cmdq->mbox.chans)
+ return -ENOMEM;
+
+ cmdq->mbox.num_chans = CMDQ_THR_MAX_COUNT;
+ cmdq->mbox.ops = &cmdq_mbox_chan_ops;
+ cmdq->mbox.of_xlate = cmdq_xlate;
+
+ /* make use of TXDONE_BY_ACK */
+ cmdq->mbox.txdone_irq = false;
+ cmdq->mbox.txdone_poll = false;
+
+ for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
+ cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +
+ CMDQ_THR_SIZE * i;
+ INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);
+ init_timer(&cmdq->thread[i].timeout);
+ cmdq->thread[i].timeout.function = cmdq_thread_handle_timeout;
+ cmdq->thread[i].timeout.data = (unsigned long)&cmdq->thread[i];
+ cmdq->mbox.chans[i].con_priv = &cmdq->thread[i];
+ }
+
+ err = mbox_controller_register(&cmdq->mbox);
+ if (err < 0) {
+ dev_err(dev, "failed to register mailbox: %d\n", err);
+ return err;
+ }
+
+ platform_set_drvdata(pdev, cmdq);
+ WARN_ON(clk_prepare(cmdq->clock) < 0);
+ return 0;
+}
+
+static const struct of_device_id cmdq_of_ids[] = {
+ {.compatible = "mediatek,mt8173-gce",},
+ {}
+};
+
+static struct platform_driver cmdq_drv = {
+ .probe = cmdq_probe,
+ .remove = cmdq_remove,
+ .driver = {
+ .name = "mtk_cmdq",
+ .of_match_table = cmdq_of_ids,
+ }
+};
+
+builtin_platform_driver(cmdq_drv);
diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
index 0a4ea80..94651ed 100644
--- a/drivers/soc/mediatek/Kconfig
+++ b/drivers/soc/mediatek/Kconfig
@@ -1,6 +1,17 @@
#
# MediaTek SoC drivers
#
+config MTK_CMDQ
+ bool "MediaTek CMDQ Support"
+ depends on ARM64 && ( ARCH_MEDIATEK || COMPILE_TEST )
+ select MTK_CMDQ_MBOX
+ select MTK_INFRACFG
+ help
+ Say yes here to add support for the MediaTek Command Queue (CMDQ)
+ driver. The CMDQ is used to help read/write registers with critical
+ time limitation, such as updating display configuration during the
+ vblank.
+
config MTK_INFRACFG
bool "MediaTek INFRACFG Support"
depends on ARCH_MEDIATEK || COMPILE_TEST
diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
index 12998b0..64ce5ee 100644
--- a/drivers/soc/mediatek/Makefile
+++ b/drivers/soc/mediatek/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c
new file mode 100644
index 0000000..f4002bf
--- /dev/null
+++ b/drivers/soc/mediatek/mtk-cmdq-helper.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/mailbox/mtk-cmdq-mailbox.h>
+#include <linux/of_address.h>
+
+#define CMDQ_SUBSYS_SHIFT 16
+#define CMDQ_ARG_A_WRITE_MASK 0xffff
+#define CMDQ_WRITE_ENABLE_MASK BIT(0)
+#define CMDQ_EOC_IRQ_EN BIT(0)
+#define CMDQ_EOC_CMD ((u64)((CMDQ_CODE_EOC << CMDQ_OP_CODE_SHIFT)) \
+ << 32 | CMDQ_EOC_IRQ_EN)
+
+struct cmdq_subsys {
+ u32 base;
+ int id;
+};
+
+static const struct cmdq_subsys gce_subsys[] = {
+ {0x1400, 1},
+ {0x1401, 2},
+ {0x1402, 3},
+};
+
+static int cmdq_subsys_base_to_id(u32 base)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(gce_subsys); i++)
+ if (gce_subsys[i].base == base)
+ return gce_subsys[i].id;
+ return -EFAULT;
+}
+
+static int cmdq_pkt_realloc_cmd_buffer(struct cmdq_pkt *pkt, size_t size)
+{
+ void *new_buf;
+
+ new_buf = krealloc(pkt->va_base, size, GFP_KERNEL | __GFP_ZERO);
+ if (!new_buf)
+ return -ENOMEM;
+ pkt->va_base = new_buf;
+ pkt->buf_size = size;
+ return 0;
+}
+
+struct cmdq_base *cmdq_register_device(struct device *dev)
+{
+ struct cmdq_base *cmdq_base;
+ struct resource res;
+ int subsys;
+ u32 base;
+
+ if (of_address_to_resource(dev->of_node, 0, &res))
+ return NULL;
+ base = (u32)res.start;
+
+ subsys = cmdq_subsys_base_to_id(base >> 16);
+ if (subsys < 0)
+ return NULL;
+
+ cmdq_base = devm_kmalloc(dev, sizeof(*cmdq_base), GFP_KERNEL);
+ if (!cmdq_base)
+ return NULL;
+ cmdq_base->subsys = subsys;
+ cmdq_base->base = base;
+
+ return cmdq_base;
+}
+EXPORT_SYMBOL(cmdq_register_device);
+
+struct cmdq_client *cmdq_mbox_create(struct device *dev, int index)
+{
+ struct cmdq_client *client;
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ client->client.dev = dev;
+ client->client.tx_block = false;
+ client->chan = mbox_request_channel(&client->client, index);
+ return client;
+}
+EXPORT_SYMBOL(cmdq_mbox_create);
+
+void cmdq_mbox_destroy(struct cmdq_client *client)
+{
+ mbox_free_channel(client->chan);
+ kfree(client);
+}
+EXPORT_SYMBOL(cmdq_mbox_destroy);
+
+int cmdq_pkt_create(struct cmdq_pkt **pkt_ptr)
+{
+ struct cmdq_pkt *pkt;
+ int err;
+
+ pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
+ if (!pkt)
+ return -ENOMEM;
+ err = cmdq_pkt_realloc_cmd_buffer(pkt, PAGE_SIZE);
+ if (err < 0) {
+ kfree(pkt);
+ return err;
+ }
+ *pkt_ptr = pkt;
+ return 0;
+}
+EXPORT_SYMBOL(cmdq_pkt_create);
+
+void cmdq_pkt_destroy(struct cmdq_pkt *pkt)
+{
+ kfree(pkt->va_base);
+ kfree(pkt);
+}
+EXPORT_SYMBOL(cmdq_pkt_destroy);
+
+static bool cmdq_pkt_is_finalized(struct cmdq_pkt *pkt)
+{
+ u64 *expect_eoc;
+
+ if (pkt->cmd_buf_size < CMDQ_INST_SIZE << 1)
+ return false;
+
+ expect_eoc = pkt->va_base + pkt->cmd_buf_size - (CMDQ_INST_SIZE << 1);
+ if (*expect_eoc == CMDQ_EOC_CMD)
+ return true;
+
+ return false;
+}
+
+static int cmdq_pkt_append_command(struct cmdq_pkt *pkt, enum cmdq_code code,
+ u32 arg_a, u32 arg_b)
+{
+ u64 *cmd_ptr;
+ int err;
+
+ if (WARN_ON(cmdq_pkt_is_finalized(pkt)))
+ return -EBUSY;
+ if (unlikely(pkt->cmd_buf_size + CMDQ_INST_SIZE > pkt->buf_size)) {
+ err = cmdq_pkt_realloc_cmd_buffer(pkt, pkt->buf_size << 1);
+ if (err < 0)
+ return err;
+ }
+ cmd_ptr = pkt->va_base + pkt->cmd_buf_size;
+ (*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a) << 32 | arg_b;
+ pkt->cmd_buf_size += CMDQ_INST_SIZE;
+ return 0;
+}
+
+int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, struct cmdq_base *base,
+ u32 offset)
+{
+ u32 arg_a = ((base->base + offset) & CMDQ_ARG_A_WRITE_MASK) |
+ (base->subsys << CMDQ_SUBSYS_SHIFT);
+ return cmdq_pkt_append_command(pkt, CMDQ_CODE_WRITE, arg_a, value);
+}
+EXPORT_SYMBOL(cmdq_pkt_write);
+
+int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value,
+ struct cmdq_base *base, u32 offset, u32 mask)
+{
+ u32 offset_mask = offset;
+ int err;
+
+ if (mask != 0xffffffff) {
+ err = cmdq_pkt_append_command(pkt, CMDQ_CODE_MASK, 0, ~mask);
+ if (err < 0)
+ return err;
+ offset_mask |= CMDQ_WRITE_ENABLE_MASK;
+ }
+ return cmdq_pkt_write(pkt, value, base, offset_mask);
+}
+EXPORT_SYMBOL(cmdq_pkt_write_mask);
+
+static const u32 cmdq_event_value[CMDQ_MAX_EVENT] = {
+ /* Display start of frame(SOF) events */
+ [CMDQ_EVENT_DISP_OVL0_SOF] = 11,
+ [CMDQ_EVENT_DISP_OVL1_SOF] = 12,
+ [CMDQ_EVENT_DISP_RDMA0_SOF] = 13,
+ [CMDQ_EVENT_DISP_RDMA1_SOF] = 14,
+ [CMDQ_EVENT_DISP_RDMA2_SOF] = 15,
+ [CMDQ_EVENT_DISP_WDMA0_SOF] = 16,
+ [CMDQ_EVENT_DISP_WDMA1_SOF] = 17,
+ /* Display end of frame(EOF) events */
+ [CMDQ_EVENT_DISP_OVL0_EOF] = 39,
+ [CMDQ_EVENT_DISP_OVL1_EOF] = 40,
+ [CMDQ_EVENT_DISP_RDMA0_EOF] = 41,
+ [CMDQ_EVENT_DISP_RDMA1_EOF] = 42,
+ [CMDQ_EVENT_DISP_RDMA2_EOF] = 43,
+ [CMDQ_EVENT_DISP_WDMA0_EOF] = 44,
+ [CMDQ_EVENT_DISP_WDMA1_EOF] = 45,
+ /* Mutex end of frame(EOF) events */
+ [CMDQ_EVENT_MUTEX0_STREAM_EOF] = 53,
+ [CMDQ_EVENT_MUTEX1_STREAM_EOF] = 54,
+ [CMDQ_EVENT_MUTEX2_STREAM_EOF] = 55,
+ [CMDQ_EVENT_MUTEX3_STREAM_EOF] = 56,
+ [CMDQ_EVENT_MUTEX4_STREAM_EOF] = 57,
+ /* Display underrun events */
+ [CMDQ_EVENT_DISP_RDMA0_UNDERRUN] = 63,
+ [CMDQ_EVENT_DISP_RDMA1_UNDERRUN] = 64,
+ [CMDQ_EVENT_DISP_RDMA2_UNDERRUN] = 65,
+};
+
+int cmdq_pkt_wfe(struct cmdq_pkt *pkt, enum cmdq_event event)
+{
+ u32 arg_b;
+
+ if (event >= CMDQ_MAX_EVENT || event < 0)
+ return -EINVAL;
+
+ /*
+ * WFE arg_b
+ * bit 0-11: wait value
+ * bit 15: 1 - wait, 0 - no wait
+ * bit 16-27: update value
+ * bit 31: 1 - update, 0 - no update
+ */
+ arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
+ return cmdq_pkt_append_command(pkt, CMDQ_CODE_WFE,
+ cmdq_event_value[event], arg_b);
+}
+EXPORT_SYMBOL(cmdq_pkt_wfe);
+
+int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, enum cmdq_event event)
+{
+ if (event >= CMDQ_MAX_EVENT || event < 0)
+ return -EINVAL;
+
+ return cmdq_pkt_append_command(pkt, CMDQ_CODE_WFE,
+ cmdq_event_value[event], CMDQ_WFE_UPDATE);
+}
+EXPORT_SYMBOL(cmdq_pkt_clear_event);
+
+static int cmdq_pkt_finalize(struct cmdq_pkt *pkt)
+{
+ int err;
+
+ if (cmdq_pkt_is_finalized(pkt))
+ return 0;
+
+ /* insert EOC and generate IRQ for each command iteration */
+ err = cmdq_pkt_append_command(pkt, CMDQ_CODE_EOC, 0, CMDQ_EOC_IRQ_EN);
+ if (err < 0)
+ return err;
+
+ /* JUMP to end */
+ err = cmdq_pkt_append_command(pkt, CMDQ_CODE_JUMP, 0, CMDQ_JUMP_PASS);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+int cmdq_pkt_flush_async(struct cmdq_client *client, struct cmdq_pkt *pkt,
+ cmdq_async_flush_cb cb, void *data)
+{
+ int err;
+
+ err = cmdq_pkt_finalize(pkt);
+ if (err < 0)
+ return err;
+
+ pkt->cb.cb = cb;
+ pkt->cb.data = data;
+
+ mbox_send_message(client->chan, pkt);
+ /* We can send next packet immediately, so just call txdone. */
+ mbox_client_txdone(client->chan, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL(cmdq_pkt_flush_async);
+
+struct cmdq_flush_completion {
+ struct completion cmplt;
+ bool err;
+};
+
+static void cmdq_pkt_flush_cb(struct cmdq_cb_data data)
+{
+ struct cmdq_flush_completion *cmplt = data.data;
+
+ cmplt->err = data.err;
+ complete(&cmplt->cmplt);
+}
+
+int cmdq_pkt_flush(struct cmdq_client *client, struct cmdq_pkt *pkt)
+{
+ struct cmdq_flush_completion cmplt;
+ int err;
+
+ init_completion(&cmplt.cmplt);
+ err = cmdq_pkt_flush_async(client, pkt, cmdq_pkt_flush_cb, &cmplt);
+ if (err < 0)
+ return err;
+ wait_for_completion(&cmplt.cmplt);
+ return cmplt.err ? -EFAULT : 0;
+}
+EXPORT_SYMBOL(cmdq_pkt_flush);
diff --git a/include/linux/mailbox/mtk-cmdq-mailbox.h b/include/linux/mailbox/mtk-cmdq-mailbox.h
new file mode 100644
index 0000000..131b8b3
--- /dev/null
+++ b/include/linux/mailbox/mtk-cmdq-mailbox.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MTK_CMDQ_MAILBOX_H__
+#define __MTK_CMDQ_MAILBOX_H__
+
+#include <linux/slab.h>
+#include <linux/soc/mediatek/mtk-cmdq.h>
+
+#define CMDQ_INST_SIZE 8 /* instruction is 64-bit */
+#define CMDQ_OP_CODE_SHIFT 24
+#define CMDQ_JUMP_PASS CMDQ_INST_SIZE
+
+#define CMDQ_WFE_UPDATE BIT(31)
+#define CMDQ_WFE_WAIT BIT(15)
+#define CMDQ_WFE_WAIT_VALUE 0x1
+
+/*
+ * CMDQ_CODE_MASK:
+ * set write mask
+ * format: op mask
+ * CMDQ_CODE_WRITE:
+ * write value into target register
+ * format: op subsys address value
+ * CMDQ_CODE_JUMP:
+ * jump by offset
+ * format: op offset
+ * CMDQ_CODE_WFE:
+ * wait for event and clear
+ * it is just clear if no wait
+ * format: [wait] op event update:1 to_wait:1 wait:1
+ * [clear] op event update:1 to_wait:0 wait:0
+ * CMDQ_CODE_EOC:
+ * end of command
+ * format: op irq_flag
+ */
+enum cmdq_code {
+ CMDQ_CODE_MASK = 0x02,
+ CMDQ_CODE_WRITE = 0x04,
+ CMDQ_CODE_JUMP = 0x10,
+ CMDQ_CODE_WFE = 0x20,
+ CMDQ_CODE_EOC = 0x40,
+};
+
+struct cmdq_task_cb {
+ cmdq_async_flush_cb cb;
+ void *data;
+};
+
+struct cmdq_pkt {
+ void *va_base;
+ size_t cmd_buf_size; /* command occupied size */
+ size_t buf_size; /* real buffer size */
+ struct cmdq_task_cb cb;
+};
+
+#endif /* __MTK_CMDQ_MAILBOX_H__ */
diff --git a/include/linux/soc/mediatek/mtk-cmdq.h b/include/linux/soc/mediatek/mtk-cmdq.h
new file mode 100644
index 0000000..7320837
--- /dev/null
+++ b/include/linux/soc/mediatek/mtk-cmdq.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MTK_CMDQ_H__
+#define __MTK_CMDQ_H__
+
+#include <linux/mailbox_client.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+/* display events in command queue(CMDQ) */
+enum cmdq_event {
+ /* Display start of frame(SOF) events */
+ CMDQ_EVENT_DISP_OVL0_SOF,
+ CMDQ_EVENT_DISP_OVL1_SOF,
+ CMDQ_EVENT_DISP_RDMA0_SOF,
+ CMDQ_EVENT_DISP_RDMA1_SOF,
+ CMDQ_EVENT_DISP_RDMA2_SOF,
+ CMDQ_EVENT_DISP_WDMA0_SOF,
+ CMDQ_EVENT_DISP_WDMA1_SOF,
+ /* Display end of frame(EOF) events */
+ CMDQ_EVENT_DISP_OVL0_EOF,
+ CMDQ_EVENT_DISP_OVL1_EOF,
+ CMDQ_EVENT_DISP_RDMA0_EOF,
+ CMDQ_EVENT_DISP_RDMA1_EOF,
+ CMDQ_EVENT_DISP_RDMA2_EOF,
+ CMDQ_EVENT_DISP_WDMA0_EOF,
+ CMDQ_EVENT_DISP_WDMA1_EOF,
+ /* Mutex end of frame(EOF) events */
+ CMDQ_EVENT_MUTEX0_STREAM_EOF,
+ CMDQ_EVENT_MUTEX1_STREAM_EOF,
+ CMDQ_EVENT_MUTEX2_STREAM_EOF,
+ CMDQ_EVENT_MUTEX3_STREAM_EOF,
+ CMDQ_EVENT_MUTEX4_STREAM_EOF,
+ /* Display underrun events */
+ CMDQ_EVENT_DISP_RDMA0_UNDERRUN,
+ CMDQ_EVENT_DISP_RDMA1_UNDERRUN,
+ CMDQ_EVENT_DISP_RDMA2_UNDERRUN,
+ /* Keep this at the end */
+ CMDQ_MAX_EVENT,
+};
+
+struct cmdq_cb_data {
+ bool err;
+ void *data;
+};
+
+typedef void (*cmdq_async_flush_cb)(struct cmdq_cb_data data);
+
+struct cmdq_pkt;
+
+struct cmdq_base {
+ int subsys;
+ u32 base;
+};
+
+struct cmdq_client {
+ struct mbox_client client;
+ struct mbox_chan *chan;
+};
+
+/**
+ * cmdq_register_device() - register device which needs CMDQ
+ * @dev: device for CMDQ to access its registers
+ *
+ * Return: cmdq_base pointer or NULL for failed
+ */
+struct cmdq_base *cmdq_register_device(struct device *dev);
+
+/**
+ * cmdq_mbox_create() - create CMDQ mailbox client and channel
+ * @dev: device of CMDQ mailbox client
+ * @index: index of CMDQ mailbox channel
+ *
+ * Return: CMDQ mailbox client pointer
+ */
+struct cmdq_client *cmdq_mbox_create(struct device *dev, int index);
+
+/**
+ * cmdq_mbox_destroy() - destroy CMDQ mailbox client and channel
+ * @client: the CMDQ mailbox client
+ */
+void cmdq_mbox_destroy(struct cmdq_client *client);
+
+/**
+ * cmdq_pkt_create() - create a CMDQ packet
+ * @pkt_ptr: CMDQ packet pointer to retrieve cmdq_pkt
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_pkt_create(struct cmdq_pkt **pkt_ptr);
+
+/**
+ * cmdq_pkt_destroy() - destroy the CMDQ packet
+ * @pkt: the CMDQ packet
+ */
+void cmdq_pkt_destroy(struct cmdq_pkt *pkt);
+
+/**
+ * cmdq_pkt_write() - append write command to the CMDQ packet
+ * @pkt: the CMDQ packet
+ * @value: the specified target register value
+ * @base: the CMDQ base
+ * @offset: register offset from module base
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value,
+ struct cmdq_base *base, u32 offset);
+
+/**
+ * cmdq_pkt_write_mask() - append write command with mask to the CMDQ packet
+ * @pkt: the CMDQ packet
+ * @value: the specified target register value
+ * @base: the CMDQ base
+ * @offset: register offset from module base
+ * @mask: the specified target register mask
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value,
+ struct cmdq_base *base, u32 offset, u32 mask);
+
+/**
+ * cmdq_pkt_wfe() - append wait for event command to the CMDQ packet
+ * @pkt: the CMDQ packet
+ * @event: the desired event type to "wait and CLEAR"
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_pkt_wfe(struct cmdq_pkt *pkt, enum cmdq_event event);
+
+/**
+ * cmdq_pkt_clear_event() - append clear event command to the CMDQ packet
+ * @pkt: the CMDQ packet
+ * @event: the desired event to be cleared
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, enum cmdq_event event);
+
+/**
+ * cmdq_pkt_flush() - trigger CMDQ to execute the CMDQ packet
+ * @client: the CMDQ mailbox client
+ * @pkt: the CMDQ packet
+ *
+ * Return: 0 for success; else the error code is returned
+ *
+ * Trigger CMDQ to execute the CMDQ packet. Note that this is a
+ * synchronous flush function. When the function returned, the recorded
+ * commands have been done.
+ */
+int cmdq_pkt_flush(struct cmdq_client *client, struct cmdq_pkt *pkt);
+
+/**
+ * cmdq_pkt_flush_async() - trigger CMDQ to asynchronously execute the CMDQ
+ * packet and call back at the end of done packet
+ * @client: the CMDQ mailbox client
+ * @pkt: the CMDQ packet
+ * @cb: called at the end of done packet
+ * @data: this data will pass back to cb
+ *
+ * Return: 0 for success; else the error code is returned
+ *
+ * Trigger CMDQ to asynchronously execute the CMDQ packet and call back
+ * at the end of done packet. Note that this is an ASYNC function. When the
+ * function returned, it may or may not be finished.
+ */
+int cmdq_pkt_flush_async(struct cmdq_client *client, struct cmdq_pkt *pkt,
+ cmdq_async_flush_cb cb, void *data);
+
+#endif /* __MTK_CMDQ_H__ */
--
1.9.1
^ permalink raw reply related
* [PATCH v15 1/4] dt-bindings: soc: Add documentation for the MediaTek GCE unit
From: HS Liao @ 2016-10-17 10:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476699117-3001-1-git-send-email-hs.liao@mediatek.com>
This adds documentation for the MediaTek Global Command Engine (GCE) unit
found in MT8173 SoCs.
Signed-off-by: HS Liao <hs.liao@mediatek.com>
Acked-by: Rob Herring <robh@kernel.org>
---
.../devicetree/bindings/mailbox/mtk-gce.txt | 43 ++++++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mailbox/mtk-gce.txt
diff --git a/Documentation/devicetree/bindings/mailbox/mtk-gce.txt b/Documentation/devicetree/bindings/mailbox/mtk-gce.txt
new file mode 100644
index 0000000..d2d3ccb
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/mtk-gce.txt
@@ -0,0 +1,43 @@
+MediaTek GCE
+===============
+
+The Global Command Engine (GCE) is used to help read/write registers with
+critical time limitation, such as updating display configuration during the
+vblank. The GCE can be used to implement the Command Queue (CMDQ) driver.
+
+CMDQ driver uses mailbox framework for communication. Please refer to
+mailbox.txt for generic information about mailbox device-tree bindings.
+
+Required properties:
+- compatible: Must be "mediatek,mt8173-gce"
+- reg: Address range of the GCE unit
+- interrupts: The interrupt signal from the GCE block
+- clock: Clocks according to the common clock binding
+- clock-names: Must be "gce" to stand for GCE clock
+- #mbox-cells: Should be 2
+
+Required properties for a client device:
+- mboxes: client use mailbox to communicate with GCE, it should have this
+ property and list of phandle, mailbox channel specifiers, and atomic
+ execution flag.
+
+Example:
+
+ gce: gce at 10212000 {
+ compatible = "mediatek,mt8173-gce";
+ reg = <0 0x10212000 0 0x1000>;
+ interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&infracfg CLK_INFRA_GCE>;
+ clock-names = "gce";
+
+ #mbox-cells = <2>;
+ };
+
+Example for a client device:
+
+ mmsys: clock-controller at 14000000 {
+ compatible = "mediatek,mt8173-mmsys";
+ mboxes = <&gce 0 1 /* main display with atomic execution */
+ &gce 1 1>; /* sub display with atomic execution */
+ ...
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH v15 0/4] Mediatek MT8173 CMDQ support
From: HS Liao @ 2016-10-17 10:11 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
This is Mediatek MT8173 Command Queue(CMDQ) driver. The CMDQ is used
to help write registers with critical time limitation, such as
updating display configuration during the vblank. It controls Global
Command Engine (GCE) hardware to achieve this requirement.
These patches have a build dependency on top of v4.9-rc1.
Changes since v14:
- split driver into mtk-cmdq-mailbox and mtk-cmdq-helper
- split cmdq_task into cmdq_pkt for client and cmdq_task for controller
- remove mailbox_controller.h from mtk-cmdq.h
- replace msleep() by schedule()
- replace irq_of_parse_and_map() by platform_get_irq()
- remove .owner = THIS_MODULE
- rebase to Linux v4.9-rc1
Best regards,
HS Liao
HS Liao (4):
dt-bindings: soc: Add documentation for the MediaTek GCE unit
CMDQ: Mediatek CMDQ driver
arm64: dts: mt8173: Add GCE node
CMDQ: save energy
.../devicetree/bindings/mailbox/mtk-gce.txt | 43 ++
arch/arm64/boot/dts/mediatek/mt8173.dtsi | 10 +
drivers/mailbox/Kconfig | 10 +
drivers/mailbox/Makefile | 2 +
drivers/mailbox/mtk-cmdq-mailbox.c | 634 +++++++++++++++++++++
drivers/soc/mediatek/Kconfig | 11 +
drivers/soc/mediatek/Makefile | 1 +
drivers/soc/mediatek/mtk-cmdq-helper.c | 310 ++++++++++
include/linux/mailbox/mtk-cmdq-mailbox.h | 67 +++
include/linux/soc/mediatek/mtk-cmdq.h | 182 ++++++
10 files changed, 1270 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mailbox/mtk-gce.txt
create mode 100644 drivers/mailbox/mtk-cmdq-mailbox.c
create mode 100644 drivers/soc/mediatek/mtk-cmdq-helper.c
create mode 100644 include/linux/mailbox/mtk-cmdq-mailbox.h
create mode 100644 include/linux/soc/mediatek/mtk-cmdq.h
--
1.9.1
^ permalink raw reply
* PROBLEM: DWC3 USB 3.0 not working on Odroid-XU4 with Exynos 5422
From: Vivek Gautam @ 2016-10-17 9:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <874m4btn2m.fsf@linux.intel.com>
On 10/17/2016 01:38 PM, Felipe Balbi wrote:
> Hi,
>
> Michael Niew?hner <linux@mniewoehner.de> writes:
>> Hi Felipe,
>> On Fri, 2016-10-07 at 22:26 +0200, Michael Niew?hner wrote:
>>> Hi Felipe,
>>>
>>> On Fr, 2016-10-07 at 10:42 +0300, Felipe Balbi wrote:
>>>> Hi,
>>>>
>>>> Michael Niew?hner <linux@mniewoehner.de> writes:
>>>>>> The clocks are same across working/non-working.
>>>>>> Is it possible to bisect the commit that's causing hang for 4.8x ?
>>>>>
>>>>> [c499ff71ff2a281366c6ec7a904c547d806cbcd1] usb: dwc3: core: re-factor init and exit paths
>>>>> This patch causes both the hang on reboot and the lsusb hang.
>>>> How to reproduce? Why don't we see this on x86 and TI boards? I'm
>>>> guessing this is failed bisection, as I can't see anything in that
>>>> commit that would cause reboot hang. Also, that code path is *NOT*
>>>> executed when you run lsusb.
>>>>
>>> I've tested this procedure multiple times to be sure:
>>>
>>> - checkout c499ff71, compile, boot the odroid
>>> - run lsusb -v => lsusb hangs, can't terminate with ctrl-c
>>> - hard reset, after boot run poweroff or reboot => board does not completely power off / reboot (see log below)
>>> - revert c499ff71, mrproper, compile, boot the odroid
>>> - run lsusb -v => shows full output, not hanging
>>> - run reboot or poweroff => board powers off / reboots just fine
>>>
>>>
>>> dmesg poweroff not working:
>>> ...
>>> [ 120.733519] systemd-journald[144]: systemd-journald stopped as pid 144
>>> [ 120.742663] systemd-shutdown[1]: Sending SIGKILL to remaining processes...
>>> [ 120.769212] systemd-shutdown[1]: Unmounting file systems.
>>> [ 120.773713] systemd-shutdown[1]: Unmounting /sys/kernel/debug.
>>> [ 120.827211] systemd-shutdown[1]: Unmounting /dev/mqueue.
>>> [ 121.081672] EXT4-fs (mmcblk1p2): re-mounted. Opts: (null)
>>> [ 121.091687] EXT4-fs (mmcblk1p2): re-mounted. Opts: (null)
>>> [ 121.095608] EXT4-fs (mmcblk1p2): re-mounted. Opts: (null)
>>> [ 121.101014] systemd-shutdown[1]: All filesystems unmounted.
>>> [ 121.106523] systemd-shutdown[1]: Deactivating swaps.
>>> [ 121.111585] systemd-shutdown[1]: All swaps deactivated.
>>> [ 121.116661] systemd-shutdown[1]: Detaching loop devices.
>>> [ 121.126395] systemd-shutdown[1]: All loop devices detached.
>>> [ 121.130525] systemd-shutdown[1]: Detaching DM devices.
>>> [ 121.135824] systemd-shutdown[1]: All DM devices detached.
>>> [ 121.166327] systemd-shutdown[1]: /lib/systemd/system-shutdown succeeded.
>>> [ 121.171739] systemd-shutdown[1]: Powering off.
>>>
>>> => at this point removing the sd card would show a message
>>> "removed mmc0" (not sure what the real message was...) so the board is not completely off.
>>>
>>>
>>> dmesg poweroff working:
>>> ...
>>> [ 120.733519] systemd-journald[144]: systemd-journald stopped as pid 144
>>> [ 120.742663] systemd-shutdown[1]: Sending SIGKILL to remaining processes...
>>> [ 120.769212] systemd-shutdown[1]: Unmounting file systems.
>>> [ 120.773713] systemd-shutdown[1]: Unmounting /sys/kernel/debug.
>>> [ 120.827211] systemd-shutdown[1]: Unmounting /dev/mqueue.
>>> [ 121.081672] EXT4-fs (mmcblk1p2): re-mounted. Opts: (null)
>>> [ 121.091687] EXT4-fs (mmcblk1p2): re-mounted. Opts: (null)
>>> [ 121.095608] EXT4-fs (mmcblk1p2): re-mounted. Opts: (null)
>>> [ 121.101014] systemd-shutdown[1]: All filesystems unmounted.
>>> [ 121.106523] systemd-shutdown[1]: Deactivating swaps.
>>> [ 121.111585] systemd-shutdown[1]: All swaps deactivated.
>>> [ 121.116661] systemd-shutdown[1]: Detaching loop devices.
>>> [ 121.126395] systemd-shutdown[1]: All loop devices detached.
>>> [ 121.130525] systemd-shutdown[1]: Detaching DM devices.
>>> [ 121.135824] systemd-shutdown[1]: All DM devices detached.
>>> [ 121.166327] systemd-shutdown[1]: /lib/systemd/system-shutdown succeeded.
>>> [ 121.171739] systemd-shutdown[1]: Powering off.
>>> [ 121.182331] rebo?
>>>
>>>
>>>
>>> Best regards
>>> Michael Niew?hner
>>
>> I did some more tests with next-20161016. Reverting / commenting out
>> one part of your patch "solves" the lsusb hang, the reboot problem
>> and also the "debounce failed" message. [1]
>> Another "solution" is to call phy_power_off before phy_power_on. [2]
>>
>> Disclaimer: I have no idea what I was doing ;-) These were just some
>> simple trial-and-error attempts that maybe help to find the real
>> cause of the problems.
>>
>> [1]
>> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
>> index 7287a76..5ef589d 100644
>> --- a/drivers/usb/dwc3/core.c
>> +++ b/drivers/usb/dwc3/core.c
>> @@ -724,6 +724,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
>> /* Adjust Frame Length */
>> dwc3_frame_length_adjustment(dwc);
>>
>> +/*
>> usb_phy_set_suspend(dwc->usb2_phy, 0);
>> usb_phy_set_suspend(dwc->usb3_phy, 0);
>> ret = phy_power_on(dwc->usb2_generic_phy);
>> @@ -733,6 +734,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
>> ret = phy_power_on(dwc->usb3_generic_phy);
>> if (ret < 0)
>> goto err3;
>> +*/
>>
>> ret = dwc3_event_buffers_setup(dwc);
>> if (ret) {
>>
>> [2]
>> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
>> index 7287a76..f6c8e13 100644
>> --- a/drivers/usb/dwc3/core.c
>> +++ b/drivers/usb/dwc3/core.c
>> @@ -726,6 +726,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
>>
>> usb_phy_set_suspend(dwc->usb2_phy, 0);
>> usb_phy_set_suspend(dwc->usb3_phy, 0);
>> + phy_power_off(dwc->usb2_generic_phy);
>> + phy_power_off(dwc->usb3_generic_phy);
> This looks like a PHY driver bug to me. Which PHY driver are you using?
>
The exynos5-usbdrd phy driver is used for exynos platforms.
Looks like something is not right with the phy driver even
after applying the phy_calibrate patches.
Michael, are you using the last set of patches for phy calibration [1]?
[1] https://lkml.org/lkml/2015/2/2/257.
Thanks
Vivek
--
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [PATCH] ARM64-cpuinfo: Combine six calls for sequence output into one seq_printf() call in c_show()
From: Matthias Brugger @ 2016-10-17 9:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <83d98772-8872-1b75-a9a5-5f08b8462e18@users.sourceforge.net>
On 16/10/16 21:03, SF Markus Elfring wrote:
> From: Markus Elfring <elfring@users.sourceforge.net>
> Date: Sun, 16 Oct 2016 20:48:28 +0200
>
> Some data were printed into a sequence by six separate function calls.
> Print the same data by a single function call instead.
>
> Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
> ---
Reviewed-by: Matthias Brugger <matthias.bgg@gmail.com>
> arch/arm64/kernel/cpuinfo.c | 19 +++++++++++--------
> 1 file changed, 11 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
> index b3d5b3e..f22687d 100644
> --- a/arch/arm64/kernel/cpuinfo.c
> +++ b/arch/arm64/kernel/cpuinfo.c
> @@ -148,14 +148,17 @@ static int c_show(struct seq_file *m, void *v)
> if (elf_hwcap & (1 << j))
> seq_printf(m, " %s", hwcap_str[j]);
> }
> - seq_puts(m, "\n");
> -
> - seq_printf(m, "CPU implementer\t: 0x%02x\n",
> - MIDR_IMPLEMENTOR(midr));
> - seq_printf(m, "CPU architecture: 8\n");
> - seq_printf(m, "CPU variant\t: 0x%x\n", MIDR_VARIANT(midr));
> - seq_printf(m, "CPU part\t: 0x%03x\n", MIDR_PARTNUM(midr));
> - seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr));
> + seq_printf(m,
> + "\n"
> + "CPU implementer\t: 0x%02x\n"
> + "CPU architecture: 8\n"
> + "CPU variant\t: 0x%x\n"
> + "CPU part\t: 0x%03x\n"
> + "CPU revision\t: %d\n\n",
> + MIDR_IMPLEMENTOR(midr),
> + MIDR_VARIANT(midr),
> + MIDR_PARTNUM(midr),
> + MIDR_REVISION(midr));
> }
>
> return 0;
>
^ permalink raw reply
* [PATCH 1/2] ARM: oxnas: Add OX820 SMP support
From: Neil Armstrong @ 2016-10-17 9:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <14513531.isB50DxNJf@wuerfel>
On 10/17/2016 11:06 AM, Arnd Bergmann wrote:
> On Monday, October 17, 2016 10:43:02 AM CEST Neil Armstrong wrote:
>> +
>> + /*
>> + * This is really belt and braces; we hold unintended secondary
>> + * CPUs in the holding pen until we're ready for them. However,
>> + * since we haven't sent them a soft interrupt, they shouldn't
>> + * be there.
>> + */
>> + write_pen_release(cpu);
>> +
>> + /*
>> + * Enable GIC cpu interface in CPU Interface Control Register
>> + */
>> + writel(GIC_CPU_CTRL_ENABLE,
>> + gic_cpu_ctrl + GIC_NCPU_OFFSET(cpu) + GIC_CPU_CTRL);
>> +
>> + /*
>> + * Send the secondary CPU a soft interrupt, thereby causing
>> + * the boot monitor to read the system wide flags register,
>> + * and branch to the address found there.
>> + */
>> +
>> + arch_send_wakeup_ipi_mask(cpumask_of(cpu));
>> + timeout = jiffies + (1 * HZ);
>> + while (time_before(jiffies, timeout)) {
>> + smp_rmb();
>> + if (read_pen_release() == -1)
>> + break;
>> +
>> + udelay(10);
>> + }
>>
>
> This seems to have been copied from plat-versatile, but is really
> not needed here since you apparently have proper hardware support for
> starting up the CPUs.
Yes it seems.
>
> Any reason you can't just write to the cpu_ctrl register
> once and keep going without that whole holding_pen loop
> and spinlock?
I suppose but I did not find any good examples except the plat-versatile code.
I will try some simpler code.
>
> Arnd
>
Neil
^ permalink raw reply
* [PATCH 10/10] mm: replace access_process_vm() write parameter with gup_flags
From: Jesper Nilsson @ 2016-10-17 9:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-11-lstoakes@gmail.com>
On Thu, Oct 13, 2016 at 01:20:20AM +0100, Lorenzo Stoakes wrote:
> This patch removes the write parameter from access_process_vm() and replaces it
> with a gup_flags parameter as use of this function previously _implied_
> FOLL_FORCE, whereas after this patch callers explicitly pass this flag.
>
> We make this explicit as use of FOLL_FORCE can result in surprising behaviour
> (and hence bugs) within the mm subsystem.
>
> Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
> ---
> arch/cris/arch-v32/kernel/ptrace.c | 4 ++--
For the CRIS part:
Acked-by: Jesper Nilsson <jesper.nilsson@axis.com>
/^JN - Jesper Nilsson
--
Jesper Nilsson -- jesper.nilsson at axis.com
^ permalink raw reply
* [PATCH] ARM: defconfig: update U8500 defconfig
From: Linus Walleij @ 2016-10-17 9:23 UTC (permalink / raw)
To: linux-arm-kernel
Some config options like perf events and PM are now implicit,
we have an upstream driver for the AK8974, and we really
want the HRTIMER software triggers from configfs with some
of the sensors.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ARM SoC folks: please apply this wherever your usual batch of
defconfig changes go.
---
arch/arm/configs/u8500_defconfig | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig
index b7b09189f1c5..e2151a7aaf49 100644
--- a/arch/arm/configs/u8500_defconfig
+++ b/arch/arm/configs/u8500_defconfig
@@ -4,7 +4,6 @@ CONFIG_NO_HZ_IDLE=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_KALLSYMS_ALL=y
-CONFIG_PERF_EVENTS=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
# CONFIG_BLK_DEV_BSG is not set
@@ -26,7 +25,6 @@ CONFIG_CPU_IDLE=y
CONFIG_ARM_U8500_CPUIDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
-CONFIG_PM=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
@@ -108,18 +106,19 @@ CONFIG_DMADEVICES=y
CONFIG_STE_DMA40=y
CONFIG_HSEM_U8500=y
CONFIG_IIO=y
-CONFIG_IIO_BUFFER=y
+CONFIG_IIO_SW_TRIGGER=y
CONFIG_IIO_ST_ACCEL_3AXIS=y
CONFIG_IIO_ST_GYRO_3AXIS=y
CONFIG_BH1780=y
+CONFIG_AK8974=y
CONFIG_IIO_ST_MAGN_3AXIS=y
+CONFIG_IIO_HRTIMER_TRIGGER=y
CONFIG_IIO_ST_PRESS=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT2_FS_POSIX_ACL=y
CONFIG_EXT2_FS_SECURITY=y
CONFIG_EXT3_FS=y
-CONFIG_EXT4_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
--
2.7.4
^ permalink raw reply related
* [PATCH 06/10] mm: replace get_user_pages() write/force parameters with gup_flags
From: Jesper Nilsson @ 2016-10-17 9:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161013002020.3062-7-lstoakes@gmail.com>
On Thu, Oct 13, 2016 at 01:20:16AM +0100, Lorenzo Stoakes wrote:
> This patch removes the write and force parameters from get_user_pages() and
> replaces them with a gup_flags parameter to make the use of FOLL_FORCE explicit
> in callers as use of this flag can result in surprising behaviour (and hence
> bugs) within the mm subsystem.
>
> Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
> ---
> arch/cris/arch-v32/drivers/cryptocop.c | 4 +---
For the CRIS part:
Acked-by: Jesper Nilsson <jesper.nilsson@axis.com>
/^JN - Jesper Nilsson
--
Jesper Nilsson -- jesper.nilsson at axis.com
^ permalink raw reply
* [PATCH] atags_proc: add missing \n when no ATAGs
From: Kefeng Wang @ 2016-10-17 9:16 UTC (permalink / raw)
To: linux-arm-kernel
No ATAGs message is missing a \n, add it.
Signed-off-by: Kefeng Wang <wangkefeng.wang@huawei.com>
---
arch/arm/kernel/atags_proc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/kernel/atags_proc.c b/arch/arm/kernel/atags_proc.c
index 5a33790..d7af7b8 100644
--- a/arch/arm/kernel/atags_proc.c
+++ b/arch/arm/kernel/atags_proc.c
@@ -41,7 +41,7 @@ static int __init init_atags_procfs(void)
size_t size;
if (tag->hdr.tag != ATAG_CORE) {
- pr_info("No ATAGs?");
+ pr_info("No ATAGs?\n");
return -EINVAL;
}
--
1.7.12.4
^ permalink raw reply related
* [PATCH 1/2] ARM: oxnas: Add OX820 SMP support
From: Arnd Bergmann @ 2016-10-17 9:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017084303.20078-2-narmstrong@baylibre.com>
On Monday, October 17, 2016 10:43:02 AM CEST Neil Armstrong wrote:
> +
> + /*
> + * This is really belt and braces; we hold unintended secondary
> + * CPUs in the holding pen until we're ready for them. However,
> + * since we haven't sent them a soft interrupt, they shouldn't
> + * be there.
> + */
> + write_pen_release(cpu);
> +
> + /*
> + * Enable GIC cpu interface in CPU Interface Control Register
> + */
> + writel(GIC_CPU_CTRL_ENABLE,
> + gic_cpu_ctrl + GIC_NCPU_OFFSET(cpu) + GIC_CPU_CTRL);
> +
> + /*
> + * Send the secondary CPU a soft interrupt, thereby causing
> + * the boot monitor to read the system wide flags register,
> + * and branch to the address found there.
> + */
> +
> + arch_send_wakeup_ipi_mask(cpumask_of(cpu));
> + timeout = jiffies + (1 * HZ);
> + while (time_before(jiffies, timeout)) {
> + smp_rmb();
> + if (read_pen_release() == -1)
> + break;
> +
> + udelay(10);
> + }
>
This seems to have been copied from plat-versatile, but is really
not needed here since you apparently have proper hardware support for
starting up the CPUs.
Any reason you can't just write to the cpu_ctrl register
once and keep going without that whole holding_pen loop
and spinlock?
Arnd
^ permalink raw reply
* [PATCH 2/2] ARM: oxnas: Add OX820 config and makefile entry
From: Neil Armstrong @ 2016-10-17 8:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017084303.20078-1-narmstrong@baylibre.com>
Refactor the oxnas Kconfig entries among the OX810SE and OX820 configs,
and add the files to support the OX820 SMP feature.
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm/Makefile | 1 +
arch/arm/mach-oxnas/Kconfig | 30 +++++++++++++++++++++---------
2 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 6be9ee1..68312a9 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -191,6 +191,7 @@ machine-$(CONFIG_ARCH_MXS) += mxs
machine-$(CONFIG_ARCH_NETX) += netx
machine-$(CONFIG_ARCH_NOMADIK) += nomadik
machine-$(CONFIG_ARCH_NSPIRE) += nspire
+machine-$(CONFIG_ARCH_OXNAS) += oxnas
machine-$(CONFIG_ARCH_OMAP1) += omap1
machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2
machine-$(CONFIG_ARCH_ORION5X) += orion5x
diff --git a/arch/arm/mach-oxnas/Kconfig b/arch/arm/mach-oxnas/Kconfig
index 29100be..8fa4557 100644
--- a/arch/arm/mach-oxnas/Kconfig
+++ b/arch/arm/mach-oxnas/Kconfig
@@ -1,9 +1,16 @@
menuconfig ARCH_OXNAS
bool "Oxford Semiconductor OXNAS Family SoCs"
select ARCH_HAS_RESET_CONTROLLER
+ select COMMON_CLK_OXNAS
select GPIOLIB
+ select MFD_SYSCON
+ select OXNAS_RPS_TIMER
+ select PINCTRL_OXNAS
+ select RESET_CONTROLLER
+ select RESET_OXNAS
+ select VERSATILE_FPGA_IRQ
select PINCTRL
- depends on ARCH_MULTI_V5
+ depends on ARCH_MULTI_V5 || ARCH_MULTI_V6
help
Support for OxNas SoC family developed by Oxford Semiconductor.
@@ -11,16 +18,21 @@ if ARCH_OXNAS
config MACH_OX810SE
bool "Support OX810SE Based Products"
- select ARCH_HAS_RESET_CONTROLLER
- select COMMON_CLK_OXNAS
+ depends on ARCH_MULTI_V5
select CPU_ARM926T
- select MFD_SYSCON
- select OXNAS_RPS_TIMER
- select PINCTRL_OXNAS
- select RESET_CONTROLLER
- select RESET_OXNAS
- select VERSATILE_FPGA_IRQ
help
Include Support for the Oxford Semiconductor OX810SE SoC Based Products.
+config MACH_OX820
+ bool "Support OX820 Based Products"
+ depends on ARCH_MULTI_V6
+ select ARM_GIC
+ select DMA_CACHE_RWFO if SMP
+ select CPU_V6K
+ select HAVE_SMP
+ select HAVE_ARM_SCU if SMP
+ select HAVE_ARM_TWD if SMP
+ help
+ Include Support for the Oxford Semiconductor OX820 SoC Based Products.
+
endif
--
2.7.0
^ permalink raw reply related
* [PATCH 1/2] ARM: oxnas: Add OX820 SMP support
From: Neil Armstrong @ 2016-10-17 8:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017084303.20078-1-narmstrong@baylibre.com>
The Oxford Semiconductor OX820 is a ARM11MPcore based SoC sharing some
features with the OX810 earlier SoC.
This patch adds the core to wake up the second core.
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm/mach-oxnas/Makefile | 2 +
arch/arm/mach-oxnas/headsmp.S | 28 ++++++++
arch/arm/mach-oxnas/hotplug.c | 111 +++++++++++++++++++++++++++++
arch/arm/mach-oxnas/platsmp.c | 161 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 302 insertions(+)
create mode 100644 arch/arm/mach-oxnas/Makefile
create mode 100644 arch/arm/mach-oxnas/headsmp.S
create mode 100644 arch/arm/mach-oxnas/hotplug.c
create mode 100644 arch/arm/mach-oxnas/platsmp.c
diff --git a/arch/arm/mach-oxnas/Makefile b/arch/arm/mach-oxnas/Makefile
new file mode 100644
index 0000000..b625906
--- /dev/null
+++ b/arch/arm/mach-oxnas/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SMP) += platsmp.o headsmp.o
+obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-oxnas/headsmp.S b/arch/arm/mach-oxnas/headsmp.S
new file mode 100644
index 0000000..2a94dcb
--- /dev/null
+++ b/arch/arm/mach-oxnas/headsmp.S
@@ -0,0 +1,28 @@
+/*
+ * linux/arch/arm/mach-oxnas/headsmp.S
+ *
+ * Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com>
+ * Copyright (c) 2003 ARM Limited
+ * All Rights Reserved
+ *
+ * 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/linkage.h>
+#include <linux/init.h>
+
+ __INIT
+
+/*
+ * OX820 specific entry point for secondary CPUs.
+ */
+ENTRY(ox820_secondary_startup)
+ mov r4, #0
+ /* invalidate both caches and branch target cache */
+ mcr p15, 0, r4, c7, c7, 0
+ /*
+ * we've been released from the holding pen: secondary_stack
+ * should now contain the SVC stack for this core
+ */
+ b secondary_startup
diff --git a/arch/arm/mach-oxnas/hotplug.c b/arch/arm/mach-oxnas/hotplug.c
new file mode 100644
index 0000000..18fa814
--- /dev/null
+++ b/arch/arm/mach-oxnas/hotplug.c
@@ -0,0 +1,111 @@
+/*
+ * linux/arch/arm/mach-oxnas/hotplug.c
+ *
+ * Copyright (C) 2002 ARM Ltd.
+ * All Rights Reserved
+ *
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+
+#include <asm/cp15.h>
+#include <asm/smp_plat.h>
+
+static inline void cpu_enter_lowpower(void)
+{
+ unsigned int v;
+
+ asm volatile(
+ " mcr p15, 0, %1, c7, c5, 0\n"
+ " mcr p15, 0, %1, c7, c10, 4\n"
+ /*
+ * Turn off coherency
+ */
+ " mrc p15, 0, %0, c1, c0, 1\n"
+ " bic %0, %0, #0x20\n"
+ " mcr p15, 0, %0, c1, c0, 1\n"
+ " mrc p15, 0, %0, c1, c0, 0\n"
+ " bic %0, %0, %2\n"
+ " mcr p15, 0, %0, c1, c0, 0\n"
+ : "=&r" (v)
+ : "r" (0), "Ir" (CR_C)
+ : "cc");
+}
+
+static inline void cpu_leave_lowpower(void)
+{
+ unsigned int v;
+
+ asm volatile( "mrc p15, 0, %0, c1, c0, 0\n"
+ " orr %0, %0, %1\n"
+ " mcr p15, 0, %0, c1, c0, 0\n"
+ " mrc p15, 0, %0, c1, c0, 1\n"
+ " orr %0, %0, #0x20\n"
+ " mcr p15, 0, %0, c1, c0, 1\n"
+ : "=&r" (v)
+ : "Ir" (CR_C)
+ : "cc");
+}
+
+static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
+{
+ /*
+ * there is no power-control hardware on this platform, so all
+ * we can do is put the core into WFI; this is safe as the calling
+ * code will have already disabled interrupts
+ */
+ for (;;) {
+ /*
+ * here's the WFI
+ */
+ asm(".word 0xe320f003\n"
+ :
+ :
+ : "memory", "cc");
+
+ if (pen_release == cpu_logical_map(cpu)) {
+ /*
+ * OK, proper wakeup, we're done
+ */
+ break;
+ }
+
+ /*
+ * Getting here, means that we have come out of WFI without
+ * having been woken up - this shouldn't happen
+ *
+ * Just note it happening - when we're woken, we can report
+ * its occurrence.
+ */
+ (*spurious)++;
+ }
+}
+
+/*
+ * platform-specific code to shutdown a CPU
+ *
+ * Called with IRQs disabled
+ */
+void ox820_cpu_die(unsigned int cpu)
+{
+ int spurious = 0;
+
+ /*
+ * we're ready for shutdown now, so do it
+ */
+ cpu_enter_lowpower();
+ platform_do_lowpower(cpu, &spurious);
+
+ /*
+ * bring this CPU back into the world of cache
+ * coherency, and then restore interrupts
+ */
+ cpu_leave_lowpower();
+
+ if (spurious)
+ pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
+}
diff --git a/arch/arm/mach-oxnas/platsmp.c b/arch/arm/mach-oxnas/platsmp.c
new file mode 100644
index 0000000..d2ff2c4
--- /dev/null
+++ b/arch/arm/mach-oxnas/platsmp.c
@@ -0,0 +1,161 @@
+/*
+ * arch/arm/mach-oxnas/platsmp.c
+ *
+ * Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com>
+ * Copyright (C) 2002 ARM Ltd.
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cp15.h>
+#include <asm/smp_plat.h>
+#include <asm/smp_scu.h>
+
+extern void ox820_secondary_startup(void);
+extern void ox820_cpu_die(unsigned int cpu);
+
+static DEFINE_SPINLOCK(boot_lock);
+
+static void __iomem *cpu_ctrl;
+static void __iomem *gic_cpu_ctrl;
+
+#define HOLDINGPEN_CPU_OFFSET 0xc8
+#define HOLDINGPEN_LOCATION_OFFSET 0xc4
+
+#define GIC_NCPU_OFFSET(cpu) (0x100 + (cpu)*0x100)
+#define GIC_CPU_CTRL 0x00
+#define GIC_CPU_CTRL_ENABLE 1
+
+static inline void write_pen_release(int val)
+{
+ writel(val, cpu_ctrl + HOLDINGPEN_CPU_OFFSET);
+}
+
+static inline int read_pen_release(void)
+{
+ return readl(cpu_ctrl + HOLDINGPEN_CPU_OFFSET);
+}
+
+void ox820_secondary_init(unsigned int cpu)
+{
+ /*
+ * let the primary processor know we're out of the
+ * pen, then head off into the C entry point
+ */
+ write_pen_release(-1);
+
+ /*
+ * Synchronise with the boot thread.
+ */
+ spin_lock(&boot_lock);
+ spin_unlock(&boot_lock);
+}
+
+int ox820_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long timeout;
+
+ /*
+ * Set synchronisation state between this boot processor
+ * and the secondary one
+ */
+ spin_lock(&boot_lock);
+
+ /*
+ * This is really belt and braces; we hold unintended secondary
+ * CPUs in the holding pen until we're ready for them. However,
+ * since we haven't sent them a soft interrupt, they shouldn't
+ * be there.
+ */
+ write_pen_release(cpu);
+
+ /*
+ * Enable GIC cpu interface in CPU Interface Control Register
+ */
+ writel(GIC_CPU_CTRL_ENABLE,
+ gic_cpu_ctrl + GIC_NCPU_OFFSET(cpu) + GIC_CPU_CTRL);
+
+ /*
+ * Send the secondary CPU a soft interrupt, thereby causing
+ * the boot monitor to read the system wide flags register,
+ * and branch to the address found there.
+ */
+
+ arch_send_wakeup_ipi_mask(cpumask_of(cpu));
+ timeout = jiffies + (1 * HZ);
+ while (time_before(jiffies, timeout)) {
+ smp_rmb();
+ if (read_pen_release() == -1)
+ break;
+
+ udelay(10);
+ }
+
+ /*
+ * now the secondary core is starting up let it run its
+ * calibrations, then wait for it to finish
+ */
+ spin_unlock(&boot_lock);
+
+ return read_pen_release() != -1 ? -ENOSYS : 0;
+}
+
+static void __init ox820_smp_prepare_cpus(unsigned int max_cpus)
+{
+ struct device_node *np;
+ void __iomem *scu_base;
+
+ np = of_find_compatible_node(NULL, NULL, "arm,arm11mp-scu");
+ scu_base = of_iomap(np, 0);
+ of_node_put(np);
+ if (!scu_base)
+ return;
+
+ /* Remap CPU Interrupt Interface Registers */
+ np = of_find_compatible_node(NULL, NULL, "arm,arm11mp-gic");
+ gic_cpu_ctrl = of_iomap(np, 1);
+ of_node_put(np);
+ if (!gic_cpu_ctrl)
+ goto unmap_scu;
+
+ np = of_find_compatible_node(NULL, NULL, "oxsemi,ox820-sys-ctrl");
+ cpu_ctrl = of_iomap(np, 0);
+ of_node_put(np);
+ if (!cpu_ctrl)
+ goto unmap_scu;
+
+ scu_enable(scu_base);
+ flush_cache_all();
+
+ /*
+ * Write the address of secondary startup into the
+ * system-wide flags register. The BootMonitor waits
+ * until it receives a soft interrupt, and then the
+ * secondary CPU branches to this address.
+ */
+ writel(virt_to_phys(ox820_secondary_startup),
+ cpu_ctrl + HOLDINGPEN_LOCATION_OFFSET);
+
+unmap_scu:
+ iounmap(scu_base);
+}
+
+static const struct smp_operations ox820_smp_ops __initconst = {
+ .smp_prepare_cpus = ox820_smp_prepare_cpus,
+ .smp_secondary_init = ox820_secondary_init,
+ .smp_boot_secondary = ox820_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_die = ox820_cpu_die,
+#endif
+};
+
+CPU_METHOD_OF_DECLARE(ox820_smp, "oxsemi,ox820-smp", &ox820_smp_ops);
--
2.7.0
^ permalink raw reply related
* [PATCH 0/2] ARM: oxnas: Add SMP support for OX820
From: Neil Armstrong @ 2016-10-17 8:43 UTC (permalink / raw)
To: linux-arm-kernel
In order to support the SMP feature of the Oxford Semiconductor OX820 SoC,
add the necessary code to handle the wake-up, hotplug and cpu entry.
Neil Armstrong (2):
ARM: oxnas: Add OX820 SMP support
ARM: oxnas: Add OX820 config and makefile entry
arch/arm/Makefile | 1 +
arch/arm/mach-oxnas/Kconfig | 30 +++++---
arch/arm/mach-oxnas/Makefile | 2 +
arch/arm/mach-oxnas/headsmp.S | 28 ++++++++
arch/arm/mach-oxnas/hotplug.c | 111 +++++++++++++++++++++++++++++
arch/arm/mach-oxnas/platsmp.c | 161 ++++++++++++++++++++++++++++++++++++++++++
6 files changed, 324 insertions(+), 9 deletions(-)
create mode 100644 arch/arm/mach-oxnas/Makefile
create mode 100644 arch/arm/mach-oxnas/headsmp.S
create mode 100644 arch/arm/mach-oxnas/hotplug.c
create mode 100644 arch/arm/mach-oxnas/platsmp.c
--
2.7.0
^ permalink raw reply
* [PATCH] RFC: ARM: fix the uaccess crash on PB11MPCore
From: Neil Armstrong @ 2016-10-17 8:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CACRpkdYJjxVsUsFUHxhy5dgjkCvYRHFRfqJwyireysMRbrLwRg@mail.gmail.com>
On 09/12/2016 03:36 PM, Linus Walleij wrote:
> On Fri, Sep 9, 2016 at 9:57 AM, Neil Armstrong <narmstrong@baylibre.com> wrote:
>> On 07/11/2016 07:08 PM, Linus Walleij wrote:
>
>>> The following patch was sketched by Russell in response to my
>>> crashes on the PB11MPCore after the patch for software-based
>>> priviledged no access support for ARMv8.1. See this thread:
>>> http://marc.info/?l=linux-arm-kernel&m=144051749807214&w=2
>>>
>>> I am unsure what is going on, I suspect everyone involved in
>>> the discussion is. I just want to repost this to get the
>>> discussion restarted, as I still have to apply this patch
>>> with every kernel iteration to get my PB11MPCore Realview
>>> running.
>>>
>>> I also know that Oxnas has actual, mass-deployed NAS (PogoPlug
>>> Pro variants) using PB11MPCore, and that they may or may not
>>> be seeing the same issue so I want their feedback on this: do
>>> you or do you not see this with mainline, or have you not even
>>> tested?
>>>
>>> Cc: Russell King <linux@armlinux.org.uk>
>>> Cc: Will Deacon <will.deacon@arm.com>
>>> Cc: Neil Armstrong <narmstrong@baylibre.com>
>>> Fixes: a5e090acbf54 ("ARM: software-based priviledged-no-access support")
>>> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
>>> ---
>>> arch/arm/kernel/smp_tlb.c | 7 +++++++
>>> 1 file changed, 7 insertions(+)
>>
>> Hi Linus, Russell, Arnd,
>>
>> I made a quick port ox OX820 over v4.8-rc4 and when enabling CONFIG_CPU_SW_DOMAIN_PAN
>> I get the following trace at boot time :
>> [ 1.690000] Unable to handle kernel paging request at virtual address b6f23684
>> [ 1.690000] Unable to handle kernel paging request at virtual address b6f95ef4
>> [ 1.690000] pgd = c281c000
>> [ 1.690000] [b6f95ef4] *pgd=62821831, *pte=00000000, *ppte=00000000
>> [ 1.690000] Internal error: Oops: 81f [#1] SMP ARM
>> [ 1.690000] Modules linked in:
>> [ 1.690000] CPU: 1 PID: 65 Comm: mount Not tainted 4.8.0-rc4-00001-g0e9c45d-dirty #22
>> [ 1.690000] Hardware name: Generic DT based system
>> [ 1.690000] task: c2aad860 task.stack: c2814000
>> [ 1.690000] PC is at ipi_flush_tlb_page+0x34/0x44
>> [ 1.690000] LR is at on_each_cpu_mask+0x58/0x60
>> [ 1.690000] pc : [<c010d3dc>] lr : [<c017d584>] psr: 20000193
>> [ 1.690000] sp : c2815da0 ip : 00000002 fp : 00067c40
>> [ 1.690000] r10: c0702744 r9 : 67c4079f r8 : 67c9e75f
>> [ 1.690000] r7 : c2815dbc r6 : c010d3a8 r5 : c2816164 r4 : 20000113
>> [ 1.690000] r3 : 00000000 r2 : b6f95003 r1 : 00000003 r0 : 00000003
>> [ 1.690000] Flags: nzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment none
>> [ 1.690000] Control: 00c5787d Table: 6281c00a DAC: 00000051
>> [ 1.690000] Process mount (pid: 65, stack limit = 0xc2814190)
>> [ 1.690000] Stack: (0xc2815da0 to 0xc2816000)
>> [...]
>> [ 1.690000] ---[ end trace 8f1207a6d611da09 ]---
>>
>> When applying this patch, it solved the issue.
>>
>> Tested-by: Neil Armstrong <narmstrong@baylibre.com>
>
> So this problem appears on all ARM11MPcore, not just the RealView.
>
> Yours,
> Linus Walleij
>
Hi Russell,
Is there a plan to push this fix as it appears to break the Realview platform and the OX820 I am currently pushing ?
Thanks,
Neil
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox