* [PATCH] tty/serial: atmel: fix fractional baud rate computation
From: Boris Brezillon @ 2016-09-22 7:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160922070746.65nzezj3fwunu55s@pengutronix.de>
On Thu, 22 Sep 2016 09:07:46 +0200
Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de> wrote:
> On Wed, Sep 21, 2016 at 12:44:14PM +0200, Nicolas Ferre wrote:
> > From: Alexey Starikovskiy <aystarik@gmail.com>
> >
> > The problem with previous code was it rounded values in wrong
> > place and produced wrong baud rate in some cases.
> >
> > Signed-off-by: Alexey Starikovskiy <aystarik@gmail.com>
> > [nicolas.ferre at atmel.com: port to newer kernel and add commit log]
> > Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
> > ---
> > drivers/tty/serial/atmel_serial.c | 10 ++++++----
> > include/linux/atmel_serial.h | 1 +
> > 2 files changed, 7 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
> > index 5f550d9feed9..fd8aa1f4ba78 100644
> > --- a/drivers/tty/serial/atmel_serial.c
> > +++ b/drivers/tty/serial/atmel_serial.c
> > @@ -2170,13 +2170,15 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
> > * accurately. This feature is enabled only when using normal mode.
> > * baudrate = selected clock / (8 * (2 - OVER) * (CD + FP / 8))
> > * Currently, OVER is always set to 0 so we get
> > - * baudrate = selected clock (16 * (CD + FP / 8))
> > + * baudrate = selected clock / (16 * (CD + FP / 8))
> > + * then
> > + * 8 CD + FP = selected clock / (2 * baudrate)
> > */
> > if (atmel_port->has_frac_baudrate &&
> > (mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_NORMAL) {
> > - div = DIV_ROUND_CLOSEST(port->uartclk, baud);
> > - cd = div / 16;
> > - fp = DIV_ROUND_CLOSEST(div % 16, 2);
> > + div = DIV_ROUND_CLOSEST(port->uartclk, baud * 2);
> > + cd = div >> 3;
> > + fp = div & ATMEL_US_FP_MASK;
>
> given baud = 115200 and uartclk = 5414300 this results in:
>
> div = DIV_ROUND_CLOSEST(5414300, 115200 * 2) = 23
> cd = 2
> fp = 7
How about:
div = DIV_ROUND_CLOSEST(port->uartclk, baud);
cd = div / 16;
fp = (div % 16) / 2;
best_baud = port->uartclk / ((16 * cd) + (8 * fp));
/* Check if we can get a better approximation by rounding up. */
if (div % 2) {
int alt_baud, alt_fp, alt_cd;
alt_fp = fp++;
alt_cd = cd;
if (alt_fp > 7) {
alt_cd++;
alt_fp = 0;
}
alt_baud = port->uartclk / ((16 * alt_cd) + (8 *alt_fp));
if (abs(best_baud - baud) > abs(alt_baud - baud)) {
best_baud = alt_baud;
fp = alt_fp;
cd = alt_cd;
}
}
>
> which yields a rate of 5414300 / 46 = 117702.17. With cd = 3 and fp = 0
> however the resulting rate is 5414300 / 48 = 112797.92.
>
> Which one is better?
>
> > } else {
> > cd = uart_get_divisor(port, baud);
> > }
> > diff --git a/include/linux/atmel_serial.h b/include/linux/atmel_serial.h
> > index f8e452aa48d7..bd2560502f3c 100644
> > --- a/include/linux/atmel_serial.h
> > +++ b/include/linux/atmel_serial.h
> > @@ -119,6 +119,7 @@
> > #define ATMEL_US_BRGR 0x20 /* Baud Rate Generator Register */
> > #define ATMEL_US_CD GENMASK(15, 0) /* Clock Divider */
> > #define ATMEL_US_FP_OFFSET 16 /* Fractional Part */
> > +#define ATMEL_US_FP_MASK 0x7
>
> Is there another user of this header? If not, this can be folded into
> the driver.
>
> Best regards
> Uwe
>
^ permalink raw reply
* [PATCH v14 4/4] CMDQ: save more energy in idle
From: Jassi Brar @ 2016-09-22 7:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473039885-24009-5-git-send-email-hs.liao@mediatek.com>
On Mon, Sep 5, 2016 at 7:14 AM, HS Liao <hs.liao@mediatek.com> wrote:
> 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.c | 54 +++++++++++++++++++++++++++++++++++++++-------
The driver is introduced by second patch of the set, so it makes sense
to merge this patch into patch 2/4.
^ permalink raw reply
* [PATCH] clocksource/drivers/ti-32k: Prevent ftrace recursion
From: Jisheng Zhang @ 2016-09-22 7:56 UTC (permalink / raw)
To: linux-arm-kernel
Currently ti-32k can be used as a scheduler clock. We properly marked
omap_32k_read_sched_clock() as notrace but we then call another
function ti_32k_read_cycles() that _wasn't_ notrace.
Having a traceable function in the sched_clock() path leads to a
recursion within ftrace and a kernel crash.
Fix this by adding notrace attribute to the ti_32k_read_cycles()
function.
Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
---
drivers/clocksource/timer-ti-32k.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/clocksource/timer-ti-32k.c b/drivers/clocksource/timer-ti-32k.c
index 92b7e39..cf5b14e 100644
--- a/drivers/clocksource/timer-ti-32k.c
+++ b/drivers/clocksource/timer-ti-32k.c
@@ -65,7 +65,7 @@ static inline struct ti_32k *to_ti_32k(struct clocksource *cs)
return container_of(cs, struct ti_32k, cs);
}
-static cycle_t ti_32k_read_cycles(struct clocksource *cs)
+static cycle_t notrace ti_32k_read_cycles(struct clocksource *cs)
{
struct ti_32k *ti = to_ti_32k(cs);
--
2.9.3
^ permalink raw reply related
* [PATCH 2/2] musb: sunxi: Force session end on babble errors in host-mode
From: Hans de Goede @ 2016-09-22 8:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160922023344.GE3981@uda0271908>
Hi,
On 09/22/2016 05:33 AM, Bin Liu wrote:
> Hi,
>
> On Sun, Sep 18, 2016 at 06:50:18PM +0200, Hans de Goede wrote:
>> The sunxi musb has a bug where sometimes it will generate a babble
>> error on device disconnect instead of a disconnect irq. When this
>> happens the musb-controller switches from host mode to device mode
>> (it clears MUSB_DEVCTL_SESSION and sets MUSB_DEVCTL_BDEVICE) and
>> gets stuck in this state.
>>
>> Clearing this requires reporting Vbus low for 200 or more ms, but
>> on some devices Vbus is simply always high (host-only mode, no Vbus
>> control).
>>
>> This commit calls sun4i_usb_phy_force_session_end() on babble errors
>> in host-mode, fixing the musb controller being stuck in this state
>> on systems without Vbus control; and also fixes the need to unplug
>> the usb-b -> usb-a cable to get out of this state on systems with
>> Vbus control.
>>
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>> drivers/usb/musb/sunxi.c | 10 ++++++++++
>> 1 file changed, 10 insertions(+)
>>
>> diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
>> index 1408245..5079d90 100644
>> --- a/drivers/usb/musb/sunxi.c
>> +++ b/drivers/usb/musb/sunxi.c
>> @@ -192,8 +192,18 @@ static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci)
>> * normally babble never happens treat it as disconnect.
>> */
>> if ((musb->int_usb & MUSB_INTR_BABBLE) && is_host_active(musb)) {
>
> musb_interrupt() handle BABBLE in host mode, and has a glue hook
> musb_platform_recover() in musb_recover_from_babble().
>
> Maybe you can use it?
Ah yes, I looked at this at the past, but that was before the babble
recover rework by Felipe Balbi which got commited on 2015-03-10.
With this rework the sunxi code should indeed be able to use the core
recovery, and we can use the musb_platform_recover() to force a
session end.
And thinking more about you're other remark, I do think I can
actually modify the sun4i phy_set_mode callback so that it can
be used for this.
I'll post a v2 with these changes soon-ish.
Regards,
Hans
>> + struct sunxi_glue *glue =
>> + dev_get_drvdata(musb->controller->parent);
>> +
>> + dev_warn(musb->controller->parent, "babble, treating as disconnect\n");
>> +
>> musb->int_usb &= ~MUSB_INTR_BABBLE;
>> musb->int_usb |= MUSB_INTR_DISCONNECT;
>> + /*
>> + * Fix the musb controller sometimes getting stuck in
>> + * bdevice state after a babble error.
>> + */
>> + sun4i_usb_phy_force_session_end(glue->phy);
>
> As I commented in PATCH 1/2, can you somehow reuse
> sun4i_usb_phy_set_mode() instead?
>
>> }
>>
>> if ((musb->int_usb & MUSB_INTR_RESET) && !is_host_active(musb)) {
>> --
>> 2.9.3
>
> Regards,
> -Bin.
>
^ permalink raw reply
* [PATCH v14 2/4] CMDQ: Mediatek CMDQ driver
From: Jassi Brar @ 2016-09-22 8:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473039885-24009-3-git-send-email-hs.liao@mediatek.com>
On Mon, Sep 5, 2016 at 7:14 AM, HS Liao <hs.liao@mediatek.com> wrote:
> 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.c | 927 +++++++++++++++++++++++++++++++++++++++
> include/linux/mailbox/mtk-cmdq.h | 180 ++++++++
> 4 files changed, 1119 insertions(+)
> create mode 100644 drivers/mailbox/mtk-cmdq.c
> create mode 100644 include/linux/mailbox/mtk-cmdq.h
>
> diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
> index 97c3729..c987382 100644
> --- a/drivers/mailbox/Kconfig
> +++ b/drivers/mailbox/Kconfig
> @@ -132,4 +132,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
> + bool "MediaTek CMDQ Support"
> + depends on ARM64 && ( ARCH_MEDIATEK || COMPILE_TEST )
> + 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.
> endif
> diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
> index 66c38e3..eb5e04e 100644
> --- a/drivers/mailbox/Makefile
> +++ b/drivers/mailbox/Makefile
> @@ -27,3 +27,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) += mtk-cmdq.o
> diff --git a/drivers/mailbox/mtk-cmdq.c b/drivers/mailbox/mtk-cmdq.c
> new file mode 100644
> index 0000000..daf5561
> --- /dev/null
> +++ b/drivers/mailbox/mtk-cmdq.c
> @@ -0,0 +1,927 @@
> +/*
> + * 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/completion.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/errno.h>
> +#include <linux/interrupt.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/kthread.h>
> +#include <linux/mailbox/mtk-cmdq.h>
> +#include <linux/mutex.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/suspend.h>
> +#include <linux/timer.h>
> +
> +#define CMDQ_THR_MAX_COUNT 3 /* main, sub, general(misc) */
> +#define CMDQ_INST_SIZE 8 /* instruction is 64-bit */
> +#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_OP_CODE_SHIFT 24
> +#define CMDQ_SUBSYS_SHIFT 16
> +
> +#define CMDQ_ARG_A_WRITE_MASK 0xffff
> +#define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)
> +
> +#define CMDQ_WRITE_ENABLE_MASK BIT(0)
> +#define CMDQ_JUMP_BY_OFFSET 0x10000000
> +#define CMDQ_JUMP_BY_PA 0x10000001
> +#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
> +#define CMDQ_EOC_IRQ_EN BIT(0)
> +
> +/*
> + * 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_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;
> + void *va_base;
> + dma_addr_t pa_base;
> + size_t cmd_buf_size; /* command occupied size */
> + size_t buf_size; /* real buffer size */
> + bool finalized;
> + struct cmdq_thread *thread;
> + struct cmdq_task_cb cb;
> +};
> +
> +struct cmdq {
> + struct mbox_controller mbox;
> + void __iomem *base;
> + u32 irq;
> + struct cmdq_thread thread[CMDQ_THR_MAX_COUNT];
> + struct mutex task_mutex;
> + struct clk *clock;
> + bool suspended;
> +};
> +
> +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_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->va_base;
> +
> + /* let previous task jump to this task */
> + dma_sync_single_for_cpu(dev, prev_task->pa_base,
> + prev_task->cmd_buf_size, DMA_TO_DEVICE);
> + prev_task_base[CMDQ_NUM_CMD(prev_task) - 1] =
> + (u64)CMDQ_JUMP_BY_PA << 32 | task->pa_base;
> + dma_sync_single_for_device(dev, prev_task->pa_base,
> + prev_task->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->va_base;
> + int i;
> +
> + dma_sync_single_for_cpu(dev, task->pa_base, task->cmd_buf_size,
> + DMA_TO_DEVICE);
> + for (i = 0; i < CMDQ_NUM_CMD(task); 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->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_task *task, struct cmdq_thread *thread)
> +{
> + struct cmdq *cmdq = task->cmdq;
> + unsigned long curr_pa, end_pa;
> +
> + task->thread = thread;
> + 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 + task->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 + task->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;
> +
> + if (task->cb.cb) {
> + cmdq_cb_data.err = err;
> + cmdq_cb_data.data = task->cb.data;
> + task->cb.cb(cmdq_cb_data);
> + }
> + list_del(&task->list_entry);
> + dma_unmap_single(dev, task->pa_base, task->cmd_buf_size, DMA_TO_DEVICE);
> + kfree(task->va_base);
> +}
> +
> +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->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_task_realloc_cmd_buffer(struct cmdq_task *task, size_t size)
> +{
> + void *new_buf;
> +
> + new_buf = krealloc(task->va_base, size, GFP_KERNEL | __GFP_ZERO);
> + if (!new_buf)
> + return -ENOMEM;
> + task->va_base = new_buf;
> + task->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);
> +
> +int cmdq_task_create(struct device *dev, struct cmdq_task **task_ptr)
> +{
> + struct cmdq_task *task;
> + int err;
> +
> + task = kzalloc(sizeof(*task), GFP_KERNEL);
> + if (!task)
> + return -ENOMEM;
> + task->cmdq = dev_get_drvdata(dev);
> + err = cmdq_task_realloc_cmd_buffer(task, PAGE_SIZE);
> + if (err < 0) {
> + kfree(task);
> + return err;
> + }
> + *task_ptr = task;
> + return 0;
> +}
> +EXPORT_SYMBOL(cmdq_task_create);
> +
> +static int cmdq_task_append_command(struct cmdq_task *task, enum cmdq_code code,
> + u32 arg_a, u32 arg_b)
> +{
> + u64 *cmd_ptr;
> + int err;
> +
> + if (WARN_ON(task->finalized))
> + return -EBUSY;
> + if (unlikely(task->cmd_buf_size + CMDQ_INST_SIZE > task->buf_size)) {
> + err = cmdq_task_realloc_cmd_buffer(task, task->buf_size * 2);
> + if (err < 0)
> + return err;
> + }
> + cmd_ptr = task->va_base + task->cmd_buf_size;
> + (*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a) << 32 | arg_b;
> + task->cmd_buf_size += CMDQ_INST_SIZE;
> + return 0;
> +}
> +
> +int cmdq_task_write(struct cmdq_task *task, 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_task_append_command(task, CMDQ_CODE_WRITE, arg_a, value);
> +}
> +EXPORT_SYMBOL(cmdq_task_write);
> +
> +int cmdq_task_write_mask(struct cmdq_task *task, u32 value,
> + struct cmdq_base *base, u32 offset, u32 mask)
> +{
> + u32 offset_mask = offset;
> + int err;
> +
> + if (mask != 0xffffffff) {
> + err = cmdq_task_append_command(task, CMDQ_CODE_MASK, 0, ~mask);
> + if (err < 0)
> + return err;
> + offset_mask |= CMDQ_WRITE_ENABLE_MASK;
> + }
> + return cmdq_task_write(task, value, base, offset_mask);
> +}
> +EXPORT_SYMBOL(cmdq_task_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_task_wfe(struct cmdq_task *task, 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_task_append_command(task, CMDQ_CODE_WFE,
> + cmdq_event_value[event], arg_b);
> +}
> +EXPORT_SYMBOL(cmdq_task_wfe);
> +
> +int cmdq_task_clear_event(struct cmdq_task *task, enum cmdq_event event)
> +{
> + if (event >= CMDQ_MAX_EVENT || event < 0)
> + return -EINVAL;
> +
> + return cmdq_task_append_command(task, CMDQ_CODE_WFE,
> + cmdq_event_value[event], CMDQ_WFE_UPDATE);
> +}
> +EXPORT_SYMBOL(cmdq_task_clear_event);
> +
> +static int cmdq_task_finalize(struct cmdq_task *task)
> +{
> + int err;
> +
> + if (task->finalized)
> + return 0;
> +
> + /* insert EOC and generate IRQ for each command iteration */
> + err = cmdq_task_append_command(task, CMDQ_CODE_EOC, 0, CMDQ_EOC_IRQ_EN);
> + if (err < 0)
> + return err;
> +
> + /* JUMP to end */
> + err = cmdq_task_append_command(task, CMDQ_CODE_JUMP, 0, CMDQ_JUMP_PASS);
> + if (err < 0)
> + return err;
> +
> + task->finalized = true;
> + return 0;
> +}
> +
> +int cmdq_task_flush_async(struct cmdq_client *client, struct cmdq_task *task,
> + cmdq_async_flush_cb cb, void *data)
> +{
> + struct cmdq *cmdq = task->cmdq;
> + int err;
> +
> + mutex_lock(&cmdq->task_mutex);
> + if (cmdq->suspended) {
> + dev_err(cmdq->mbox.dev, "%s is called after suspended\n",
> + __func__);
> + mutex_unlock(&cmdq->task_mutex);
> + return -EPERM;
> + }
> +
> + err = cmdq_task_finalize(task);
> + if (err < 0) {
> + mutex_unlock(&cmdq->task_mutex);
> + return err;
> + }
> +
> + INIT_LIST_HEAD(&task->list_entry);
> + task->cb.cb = cb;
> + task->cb.data = data;
> + task->pa_base = dma_map_single(cmdq->mbox.dev, task->va_base,
> + task->cmd_buf_size, DMA_TO_DEVICE);
> +
> + mbox_send_message(client->chan, task);
> + /* We can send next task immediately, so just call txdone. */
> + mbox_client_txdone(client->chan, 0);
> + mutex_unlock(&cmdq->task_mutex);
> + return 0;
> +}
> +EXPORT_SYMBOL(cmdq_task_flush_async);
> +
> +struct cmdq_flush_completion {
> + struct completion cmplt;
> + bool err;
> +};
> +
> +static void cmdq_task_flush_cb(struct cmdq_cb_data data)
> +{
> + struct cmdq_flush_completion *cmplt = data.data;
> +
> + cmplt->err = data.err;
> + complete(&cmplt->cmplt);
> +}
> +
> +int cmdq_task_flush(struct cmdq_client *client, struct cmdq_task *task)
> +{
> + struct cmdq_flush_completion cmplt;
> + int err;
> +
> + init_completion(&cmplt.cmplt);
> + err = cmdq_task_flush_async(client, task, cmdq_task_flush_cb, &cmplt);
> + if (err < 0)
> + return err;
> + wait_for_completion(&cmplt.cmplt);
> + return cmplt.err ? -EFAULT : 0;
> +}
> +EXPORT_SYMBOL(cmdq_task_flush);
> +
> +void cmdq_mbox_free(struct cmdq_client *client)
> +{
> + mbox_free_channel(client->chan);
> + kfree(client);
> +}
> +EXPORT_SYMBOL(cmdq_mbox_free);
> +
All these exported functions implement the protocol, so should not be
a part of this controller driver. That should go into
drivers/soc/mediatek/
The controller driver (mtk-cmdq.c) should implement mainly the
mbox_chan_ops and mbox.of_xlate.
> +static int cmdq_suspend(struct device *dev)
> +{
> + struct cmdq *cmdq = dev_get_drvdata(dev);
> + struct cmdq_thread *thread;
> + int i;
> + bool task_running = false;
> +
> + mutex_lock(&cmdq->task_mutex);
> + cmdq->suspended = true;
> + mutex_unlock(&cmdq->task_mutex);
> +
> + 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");
> + msleep(20);
> + }
> +
> + clk_unprepare(cmdq->clock);
> + return 0;
> +}
> +
> +static int cmdq_resume(struct device *dev)
> +{
> + struct cmdq *cmdq = dev_get_drvdata(dev);
> +
> + WARN_ON(clk_prepare(cmdq->clock) < 0);
> + cmdq->suspended = false;
> + return 0;
> +}
> +
> +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 device_node *node = dev->of_node;
> + 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 = irq_of_parse_and_map(node, 0);
>
why not, 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);
> + }
> +
> + mutex_init(&cmdq->task_mutex);
> +
> + 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 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",},
> + {}
> +};
> +
> +static struct platform_driver cmdq_drv = {
> + .probe = cmdq_probe,
> + .remove = cmdq_remove,
> + .driver = {
> + .name = "mtk_cmdq",
> + .owner = THIS_MODULE,
>
please remove the unnecessary .owner field.
> + .pm = &cmdq_pm_ops,
> + .of_match_table = cmdq_of_ids,
> + }
> +};
> +
> +builtin_platform_driver(cmdq_drv);
> diff --git a/include/linux/mailbox/mtk-cmdq.h b/include/linux/mailbox/mtk-cmdq.h
> new file mode 100644
> index 0000000..c3c924d
> --- /dev/null
> +++ b/include/linux/mailbox/mtk-cmdq.h
>
The api implemented is Mediateck proprietary, so I think it should be
include/linux/soc/mediatek/cmdq.h
> @@ -0,0 +1,180 @@
> +/*
> + * 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/mailbox_controller.h>
>
Clients should not need to include mailbox_controller.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,
>
you may want to explicitly initialise the first element.
> + 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,
> +};
> +
^ permalink raw reply
* [PATCH v5 2/3] ASoC: sun4i-codec: Rename some sun7i-only registers
From: Maxime Ripard @ 2016-09-22 8:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160922071313.12891-3-dannym@scratchpost.org>
On Thu, Sep 22, 2016 at 09:13:12AM +0200, Danny Milosavljevic wrote:
> Some of the registers defined in the driver are only usable on the
> A20. Rename these registers.
>
> Signed-off-by: Danny Milosavljevic <dannym@scratchpost.org>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160922/cc7c9e50/attachment.sig>
^ permalink raw reply
* [PATCH v5 3/3] ASoC: sun4i-codec: Add custom regmap configs
From: Maxime Ripard @ 2016-09-22 8:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160922071313.12891-4-dannym@scratchpost.org>
On Thu, Sep 22, 2016 at 09:13:13AM +0200, Danny Milosavljevic wrote:
> The A20 has a few extra registers that the A10 doesn't have.
> Therefore, use different regmaps for A10 as compared to A20.
>
> Signed-off-by: Danny Milosavljevic <dannym@scratchpost.org>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160922/630d1516/attachment.sig>
^ permalink raw reply
* [PATCH v5 0/3] ASoC: sun4i-codec: Distinguish sun4i from sun7i
From: Maxime Ripard @ 2016-09-22 8:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160922071313.12891-1-dannym@scratchpost.org>
Hi,
On Thu, Sep 22, 2016 at 09:13:10AM +0200, Danny Milosavljevic wrote:
> ASoC: sun4i-codec: Introduce mechanism to detect sun7i and provide a
> different regmap different compared to sun4i Allwinner A10.
>
> The controls will be extended in a forthcoming patch - it is necessary
> to distinguish between sun4i and sun7i controls because they have different
> registers.
>
> Renamed SUN4I_CODEC_AC_SYS_VERI to SUN7I_CODEC_AC_DAC_CAL and renamed
> SUN4I_CODEC_AC_MIC_PHONE_CAL to SUN7I_CODEC_AC_MIC_PHONE_CAL because these
> are actually not present on Allwinner A10.
>
> Handle quirks by regmap config and codec and select the correct quirks
> automatically.
Usually, you would set the history of changes from one version to the
other here.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160922/2b5924b5/attachment.sig>
^ permalink raw reply
* [PATCH v5 3/5] arm64: arch_timer: Work around QorIQ Erratum A-008585
From: Scott Wood @ 2016-09-22 8:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160920093503.GA1045@leverpostej>
On Tue, 2016-09-20 at 10:35 +0100, Mark Rutland wrote:
> On Mon, Sep 19, 2016 at 02:16:00PM -0500, Scott Wood wrote:
> >
> > On Mon, 2016-09-19 at 18:07 +0100, Mark Rutland wrote:
> > >
> > > >
> > > > >
> > > > > Reconsidering my suggestion, I realise this will also affect the
> > > > > MMIO
> > > > > timers, so that doesn't work.
> > > > >
> > > > > So for the moment, I guess we have to keep
> > > > > fsl_a008585_set_next_event().
> > > > What is the problem with MMIO timers? ?needs_fsl_a008585_workaround()
> > > > should
> > > > always be false for them.
> > > As suggested, needs_fsl_a008585_workaround() takes no parameter, and
> > > set_next_event is called for both cp15/sysreg and MMIO timers. So it
> > > would either be true for all, or false for all.
> > >
> > > If it's true for all, we'd end up calling fsl_a008585_set_next_event()
> > > for the MMIO timers too.
> > There should not be any MMIO timers on a system where
> > fsl_a008585_set_next_event() returns true.
> I'm generally not keen on relying on that.
>
> For reference, are no MMIO timers implemented at all, or are they simply
> not listed in the DT today?
As far as I can tell they're not implemented, but it's possible I'm just not
looking at the right documentation. ?
I agree though that depending on that isn't particularly pretty. ?I'll stick
with the current approach for set_next_event().
-Scott
^ permalink raw reply
* [PATCH v6 1/4] arm64: arch_timer: Add device tree binding for A-008585 erratum
From: Scott Wood @ 2016-09-22 8:35 UTC (permalink / raw)
To: linux-arm-kernel
This erratum describes a bug in logic outside the core, so MIDR can't be
used to identify its presence, and reading an SoC-specific revision
register from common arch timer code would be awkward. So, describe it
in the device tree.
Signed-off-by: Scott Wood <oss@buserror.net>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/arm/arch_timer.txt | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/arch_timer.txt b/Documentation/devicetree/bindings/arm/arch_timer.txt
index e774128..ef5fbe9 100644
--- a/Documentation/devicetree/bindings/arm/arch_timer.txt
+++ b/Documentation/devicetree/bindings/arm/arch_timer.txt
@@ -25,6 +25,12 @@ to deliver its interrupts via SPIs.
- always-on : a boolean property. If present, the timer is powered through an
always-on power domain, therefore it never loses context.
+- fsl,erratum-a008585 : A boolean property. Indicates the presence of
+ QorIQ erratum A-008585, which says that reading the counter is
+ unreliable unless the same value is returned by back-to-back reads.
+ This also affects writes to the tval register, due to the implicit
+ counter read.
+
** Optional properties:
- arm,cpu-registers-not-fw-configured : Firmware does not initialize
--
2.7.4
^ permalink raw reply related
* [PATCH v6 2/4] arm64: dts: Add timer erratum property for LS2080A and LS1043A
From: Scott Wood @ 2016-09-22 8:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474533318-7796-1-git-send-email-oss@buserror.net>
Signed-off-by: Scott Wood <oss@buserror.net>
---
arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi | 1 +
arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
index e669fbd..952531d 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
@@ -123,6 +123,7 @@
<1 14 0x1>, /* Physical Non-Secure PPI */
<1 11 0x1>, /* Virtual PPI */
<1 10 0x1>; /* Hypervisor PPI */
+ fsl,erratum-a008585;
};
pmu {
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi
index 21023a3..9d3ac19 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi
@@ -195,6 +195,7 @@
<1 14 0x8>, /* Physical Non-Secure PPI, active-low */
<1 11 0x8>, /* Virtual PPI, active-low */
<1 10 0x8>; /* Hypervisor PPI, active-low */
+ fsl,erratum-a008585;
};
pmu {
--
2.7.4
^ permalink raw reply related
* [PATCH v6 3/4] arm64: arch_timer: Work around QorIQ Erratum A-008585
From: Scott Wood @ 2016-09-22 8:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474533318-7796-1-git-send-email-oss@buserror.net>
Erratum A-008585 says that the ARM generic timer counter "has the
potential to contain an erroneous value for a small number of core
clock cycles every time the timer value changes". Accesses to TVAL
(both read and write) are also affected due to the implicit counter
read. Accesses to CVAL are not affected.
The workaround is to reread TVAL and count registers until successive
reads return the same value. Writes to TVAL are replaced with an
equivalent write to CVAL.
The workaround is to reread TVAL and count registers until successive reads
return the same value, and when writing TVAL to retry until counter
reads before and after the write return the same value.
The workaround is enabled if the fsl,erratum-a008585 property is found in
the timer node in the device tree. This can be overridden with the
clocksource.arm_arch_timer.fsl-a008585 boot parameter, which allows KVM
users to enable the workaround until a mechanism is implemented to
automatically communicate this information.
This erratum can be found on LS1043A and LS2080A.
Signed-off-by: Scott Wood <oss@buserror.net>
---
v6:
- Addressed feedback from Mark Rutland
v5:
- Export arch_timer_read_ool_enabled so that get_cycles() can be called
from modules.
v4:
- Undef ARCH_TIMER_REG_READ after use
v3:
- Used cval rather than a loop for the write side of the erratum
- Added a Kconfig control
- Moved the device tree binding into its own patch
- Added erratum to silicon-errata.txt
- Changed function names to contain the erratum name
- Factored out the setting of erratum versions of set_next_event
to improve readability
- Added a comment clarifying that the timeout is arbitrary
v2:
Significant rework based on feedback, including using static_key,
disabling VDSO counter access rather than adding the workaround to the
VDSO, and uninlining the loops.
Dropped the separate property for indicating that writes to TVAL are
affected, as I believe that's just a side effect of the implicit
counter read being corrupted, and thus a chip that is affected by one
will always be affected by the other.
Dropped the arm32 portion as it seems there was confusion about whether
LS1021A is affected. Currently I am being told that it is not
affected.
I considered writing to CVAL rather than looping on TVAL writes, but
that would still have required separate set_next_event() code for the
erratum, and adding CVAL to the enum would have required a bunch of
extra handlers in switch statements (even where unused, due to compiler
warnings about unhandled enum values) including in an arm32 header. It
seemed better to avoid the arm32 interaction and new untested
accessors.
Signed-off-by: Scott Wood <oss@buserror.net>
---
Documentation/arm64/silicon-errata.txt | 2 +
Documentation/kernel-parameters.txt | 9 +++
arch/arm64/include/asm/arch_timer.h | 47 ++++++++++++++-
drivers/clocksource/Kconfig | 10 ++++
drivers/clocksource/arm_arch_timer.c | 104 +++++++++++++++++++++++++++++++++
5 files changed, 169 insertions(+), 3 deletions(-)
diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt
index 4da60b4..041e3a9 100644
--- a/Documentation/arm64/silicon-errata.txt
+++ b/Documentation/arm64/silicon-errata.txt
@@ -60,3 +60,5 @@ stable kernels.
| Cavium | ThunderX GICv3 | #23154 | CAVIUM_ERRATUM_23154 |
| Cavium | ThunderX Core | #27456 | CAVIUM_ERRATUM_27456 |
| Cavium | ThunderX SMMUv2 | #27704 | N/A |
+| | | | |
+| Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 46c030a..fb4de4d 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -698,6 +698,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
loops can be debugged more effectively on production
systems.
+ clocksource.arm_arch_timer.fsl-a008585=
+ [ARM64]
+ Format: <bool>
+ Enable/disable the workaround of Freescale/NXP
+ erratum A-008585. This can be useful for KVM
+ guests, if the guest device tree doesn't show the
+ erratum. If unspecified, the workaround is
+ enabled based on the device tree.
+
clearcpuid=BITNUM [X86]
Disable CPUID feature X for the kernel. See
arch/x86/include/asm/cpufeatures.h for the valid bit
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index 7ff386c..cddd5b7 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -24,10 +24,51 @@
#include <linux/bug.h>
#include <linux/init.h>
+#include <linux/jump_label.h>
#include <linux/types.h>
#include <clocksource/arm_arch_timer.h>
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
+extern struct static_key_false arch_timer_read_ool_enabled;
+#define needs_fsl_a008585_workaround() \
+ static_branch_unlikely(&arch_timer_read_ool_enabled)
+#else
+#define needs_fsl_a008585_workaround() false
+#endif
+
+u32 __fsl_a008585_read_cntp_tval_el0(void);
+u32 __fsl_a008585_read_cntv_tval_el0(void);
+u64 __fsl_a008585_read_cntvct_el0(void);
+
+/*
+ * The number of retries is an arbitrary value well beyond the highest number
+ * of iterations the loop has been observed to take.
+ */
+#define __fsl_a008585_read_reg(reg) ({ \
+ u64 _old, _new; \
+ int _retries = 200; \
+ \
+ do { \
+ _old = read_sysreg(reg); \
+ _new = read_sysreg(reg); \
+ _retries--; \
+ } while (unlikely(_old != _new) && _retries); \
+ \
+ WARN_ON_ONCE(!_retries); \
+ _new; \
+})
+
+#define arch_timer_unstable_reg_read(reg) \
+({ \
+ u64 _val; \
+ if (needs_fsl_a008585_workaround()) \
+ _val = __fsl_a008585_read_##reg(); \
+ else \
+ _val = read_sysreg(reg); \
+ _val; \
+})
+
/*
* These register accessors are marked inline so the compiler can
* nicely work out which register we want, and chuck away the rest of
@@ -67,14 +108,14 @@ u32 arch_timer_reg_read_cp15(int access, enum arch_timer_reg reg)
case ARCH_TIMER_REG_CTRL:
return read_sysreg(cntp_ctl_el0);
case ARCH_TIMER_REG_TVAL:
- return read_sysreg(cntp_tval_el0);
+ return arch_timer_unstable_reg_read(cntp_tval_el0);
}
} else if (access == ARCH_TIMER_VIRT_ACCESS) {
switch (reg) {
case ARCH_TIMER_REG_CTRL:
return read_sysreg(cntv_ctl_el0);
case ARCH_TIMER_REG_TVAL:
- return read_sysreg(cntv_tval_el0);
+ return arch_timer_unstable_reg_read(cntv_tval_el0);
}
}
@@ -108,7 +149,7 @@ static inline u64 arch_counter_get_cntpct(void)
static inline u64 arch_counter_get_cntvct(void)
{
isb();
- return read_sysreg(cntvct_el0);
+ return arch_timer_unstable_reg_read(cntvct_el0);
}
static inline int arch_timer_arch_init(void)
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 5677886..8a753fd 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -305,6 +305,16 @@ config ARM_ARCH_TIMER_EVTSTREAM
This must be disabled for hardware validation purposes to detect any
hardware anomalies of missing events.
+config FSL_ERRATUM_A008585
+ bool "Workaround for Freescale/NXP Erratum A-008585"
+ default y
+ depends on ARM_ARCH_TIMER && ARM64
+ help
+ This option enables a workaround for Freescale/NXP Erratum
+ A-008585 ("ARM generic timer may contain an erroneous
+ value"). The workaround will only be active if the
+ fsl,erratum-a008585 property is found in the timer node.
+
config ARM_GLOBAL_TIMER
bool "Support for the ARM global timer" if COMPILE_TEST
select CLKSRC_OF if OF
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 5770054..eb5fb41 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -94,6 +94,43 @@ early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
* Architected system timer support.
*/
+#ifdef CONFIG_FSL_ERRATUM_A008585
+DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
+EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
+
+static int fsl_a008585_enable = -1;
+
+static int __init early_fsl_a008585_cfg(char *buf)
+{
+ int ret;
+ bool val;
+
+ ret = strtobool(buf, &val);
+ if (ret)
+ return ret;
+
+ fsl_a008585_enable = val;
+ return 0;
+}
+early_param("clocksource.arm_arch_timer.fsl-a008585", early_fsl_a008585_cfg);
+
+u32 __fsl_a008585_read_cntp_tval_el0(void)
+{
+ return __fsl_a008585_read_reg(cntp_tval_el0);
+}
+
+u32 __fsl_a008585_read_cntv_tval_el0(void)
+{
+ return __fsl_a008585_read_reg(cntv_tval_el0);
+}
+
+u64 __fsl_a008585_read_cntvct_el0(void)
+{
+ return __fsl_a008585_read_reg(cntvct_el0);
+}
+EXPORT_SYMBOL(__fsl_a008585_read_cntvct_el0);
+#endif /* CONFIG_FSL_ERRATUM_A008585 */
+
static __always_inline
void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val,
struct clock_event_device *clk)
@@ -243,6 +280,40 @@ static __always_inline void set_next_event(const int access, unsigned long evt,
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
+#ifdef CONFIG_FSL_ERRATUM_A008585
+static __always_inline void fsl_a008585_set_next_event(const int access,
+ unsigned long evt, struct clock_event_device *clk)
+{
+ unsigned long ctrl;
+ u64 cval = evt + arch_counter_get_cntvct();
+
+ ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, clk);
+ ctrl |= ARCH_TIMER_CTRL_ENABLE;
+ ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
+
+ if (access == ARCH_TIMER_PHYS_ACCESS)
+ write_sysreg(cval, cntp_cval_el0);
+ else if (access == ARCH_TIMER_VIRT_ACCESS)
+ write_sysreg(cval, cntv_cval_el0);
+
+ arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
+}
+
+static int fsl_a008585_set_next_event_virt(unsigned long evt,
+ struct clock_event_device *clk)
+{
+ fsl_a008585_set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk);
+ return 0;
+}
+
+static int fsl_a008585_set_next_event_phys(unsigned long evt,
+ struct clock_event_device *clk)
+{
+ fsl_a008585_set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk);
+ return 0;
+}
+#endif /* CONFIG_FSL_ERRATUM_A008585 */
+
static int arch_timer_set_next_event_virt(unsigned long evt,
struct clock_event_device *clk)
{
@@ -271,6 +342,19 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
return 0;
}
+static void fsl_a008585_set_sne(struct clock_event_device *clk)
+{
+#ifdef CONFIG_FSL_ERRATUM_A008585
+ if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
+ return;
+
+ if (arch_timer_uses_ppi == VIRT_PPI)
+ clk->set_next_event = fsl_a008585_set_next_event_virt;
+ else
+ clk->set_next_event = fsl_a008585_set_next_event_phys;
+#endif
+}
+
static void __arch_timer_setup(unsigned type,
struct clock_event_device *clk)
{
@@ -299,6 +383,8 @@ static void __arch_timer_setup(unsigned type,
default:
BUG();
}
+
+ fsl_a008585_set_sne(clk);
} else {
clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
clk->name = "arch_mem_timer";
@@ -515,6 +601,15 @@ static void __init arch_counter_register(unsigned type)
arch_timer_read_counter = arch_counter_get_cntvct;
else
arch_timer_read_counter = arch_counter_get_cntpct;
+
+#ifdef CONFIG_FSL_ERRATUM_A008585
+ /*
+ * Don't use the vdso fastpath if errata require using
+ * the out-of-line counter accessor.
+ */
+ if (static_branch_unlikely(&arch_timer_read_ool_enabled))
+ clocksource_counter.name = "arch_sys_counter_ool";
+#endif
} else {
arch_timer_read_counter = arch_counter_get_cntvct_mem;
@@ -800,6 +895,15 @@ static int __init arch_timer_of_init(struct device_node *np)
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
+#ifdef CONFIG_FSL_ERRATUM_A008585
+ if (fsl_a008585_enable < 0)
+ fsl_a008585_enable = of_property_read_bool(np, "fsl,erratum-a008585");
+ if (fsl_a008585_enable) {
+ static_branch_enable(&arch_timer_read_ool_enabled);
+ pr_info("Enabling workaround for FSL erratum A-008585\n");
+ }
+#endif
+
/*
* If we cannot rely on firmware initializing the timer registers then
* we should use the physical timers instead.
--
2.7.4
^ permalink raw reply related
* [PATCH v6 4/4] arm/arm64: arch_timer: Use archdata to indicate vdso suitability
From: Scott Wood @ 2016-09-22 8:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474533318-7796-1-git-send-email-oss@buserror.net>
Instead of comparing the name to a magic string, use archdata to
explicitly communicate whether the arch timer is suitable for
direct vdso access.
Signed-off-by: Scott Wood <oss@buserror.net>
Acked-by: Will Deacon <will.deacon@arm.com>
---
arch/arm/Kconfig | 1 +
arch/arm/include/asm/clocksource.h | 8 ++++++++
arch/arm/kernel/vdso.c | 2 +-
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/clocksource.h | 8 ++++++++
arch/arm64/kernel/vdso.c | 2 +-
drivers/clocksource/arm_arch_timer.c | 11 +++--------
7 files changed, 23 insertions(+), 10 deletions(-)
create mode 100644 arch/arm/include/asm/clocksource.h
create mode 100644 arch/arm64/include/asm/clocksource.h
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a9c4e48..b2113c2 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1,6 +1,7 @@
config ARM
bool
default y
+ select ARCH_CLOCKSOURCE_DATA
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_HAS_ELF_RANDOMIZE
diff --git a/arch/arm/include/asm/clocksource.h b/arch/arm/include/asm/clocksource.h
new file mode 100644
index 0000000..0b350a7
--- /dev/null
+++ b/arch/arm/include/asm/clocksource.h
@@ -0,0 +1,8 @@
+#ifndef _ASM_CLOCKSOURCE_H
+#define _ASM_CLOCKSOURCE_H
+
+struct arch_clocksource_data {
+ bool vdso_direct; /* Usable for direct VDSO access? */
+};
+
+#endif
diff --git a/arch/arm/kernel/vdso.c b/arch/arm/kernel/vdso.c
index 994e971..a0affd1 100644
--- a/arch/arm/kernel/vdso.c
+++ b/arch/arm/kernel/vdso.c
@@ -270,7 +270,7 @@ static bool tk_is_cntvct(const struct timekeeper *tk)
if (!IS_ENABLED(CONFIG_ARM_ARCH_TIMER))
return false;
- if (strcmp(tk->tkr_mono.clock->name, "arch_sys_counter") != 0)
+ if (!tk->tkr_mono.clock->archdata.vdso_direct)
return false;
return true;
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 0e11c8a..8f868ec 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -4,6 +4,7 @@ config ARM64
select ACPI_GENERIC_GSI if ACPI
select ACPI_REDUCED_HARDWARE_ONLY if ACPI
select ACPI_MCFG if ACPI
+ select ARCH_CLOCKSOURCE_DATA
select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
diff --git a/arch/arm64/include/asm/clocksource.h b/arch/arm64/include/asm/clocksource.h
new file mode 100644
index 0000000..0b350a7
--- /dev/null
+++ b/arch/arm64/include/asm/clocksource.h
@@ -0,0 +1,8 @@
+#ifndef _ASM_CLOCKSOURCE_H
+#define _ASM_CLOCKSOURCE_H
+
+struct arch_clocksource_data {
+ bool vdso_direct; /* Usable for direct VDSO access? */
+};
+
+#endif
diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c
index 6225612..a2c2478 100644
--- a/arch/arm64/kernel/vdso.c
+++ b/arch/arm64/kernel/vdso.c
@@ -201,7 +201,7 @@ up_fail:
*/
void update_vsyscall(struct timekeeper *tk)
{
- u32 use_syscall = strcmp(tk->tkr_mono.clock->name, "arch_sys_counter");
+ u32 use_syscall = !tk->tkr_mono.clock->archdata.vdso_direct;
++vdso_data->tb_seq_count;
smp_wmb();
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index eb5fb41..73c487d 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -602,23 +602,18 @@ static void __init arch_counter_register(unsigned type)
else
arch_timer_read_counter = arch_counter_get_cntpct;
+ clocksource_counter.archdata.vdso_direct = true;
+
#ifdef CONFIG_FSL_ERRATUM_A008585
/*
* Don't use the vdso fastpath if errata require using
* the out-of-line counter accessor.
*/
if (static_branch_unlikely(&arch_timer_read_ool_enabled))
- clocksource_counter.name = "arch_sys_counter_ool";
+ clocksource_counter.archdata.vdso_direct = false;
#endif
} else {
arch_timer_read_counter = arch_counter_get_cntvct_mem;
-
- /* If the clocksource name is "arch_sys_counter" the
- * VDSO will attempt to read the CP15-based counter.
- * Ensure this does not happen when CP15-based
- * counter is not available.
- */
- clocksource_counter.name = "arch_mem_counter";
}
start_count = arch_timer_read_counter();
--
2.7.4
^ permalink raw reply related
* ftrace function_graph causes system crash
From: Bean Huo (beanhuo) @ 2016-09-22 8:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160921141705.6ab0be03@gandalf.local.home>
> -----Original Message-----
> From: Steven Rostedt [mailto:rostedt at goodmis.org]
> Sent: Mittwoch, 21. September 2016 20:17
> To: Jisheng Zhang <jszhang@marvell.com>
> Cc: Bean Huo (beanhuo) <beanhuo@micron.com>; Zoltan Szubbocsev
> (zszubbocsev) <zszubbocsev@micron.com>; catalin.marinas at arm.com;
> will.deacon at arm.com; rfi at lists.rocketboards.org; linux-
> kernel at vger.kernel.org; mingo at redhat.com; linux-arm-
> kernel at lists.infradead.org
> Subject: Re: ftrace function_graph causes system crash
>
> On Wed, 21 Sep 2016 17:13:07 +0800
> Jisheng Zhang <jszhang@marvell.com> wrote:
>
> > I'm not sure whether the commit d6df3576e6b4
> ("clocksource/drivers/arm_global_timer
> > : Prevent ftrace recursion") can fix this issue.
> >
> > this commit is merged since v4.3, I noticed your kernel version is v4.0
>
> BTW, yes, that would be the fix.
>
> -- Steve
>
> >
> > Thanks,
> > Jisheng
> >
> > > Do you know now how to deeply debug and trace which line is wrong
> through Ftrace?
> > >
Hi, Steven and Jisheng
Thanks to both warm-hearted guys. I merged d6df3576e6b4 patch into my kernel 4.0.
Then it is true, no cash appears again.
I have one more question that current ftrace can trace DMA latency, include mapping and unmapping?
Means I want to know when one BIO request be completed. Just like blktrace.
But blktrace can not tell me the function calling sequence.
--Bean
^ permalink raw reply
* [PATCH v2] drivers: psci: PSCI checker module
From: Lorenzo Pieralisi @ 2016-09-22 8:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <7hfuothzff.fsf@baylibre.com>
On Wed, Sep 21, 2016 at 11:37:24AM -0700, Kevin Hilman wrote:
> Kevin Brodsky <kevin.brodsky@arm.com> writes:
>
> > On arm and arm64, PSCI is one of the possible firmware interfaces
> > used for power management. This includes both turning CPUs on and off,
> > and suspending them (entering idle states).
> >
> > This patch adds a PSCI checker module that enables basic testing of
> > PSCI operations during startup. There are two main tests: CPU
> > hotplugging and suspending.
> >
> > In the hotplug tests, the hotplug API is used to turn off and on again
> > all CPUs in the system, and then all CPUs in each cluster, checking
> > the consistency of the return codes.
> >
> > In the suspend tests, a high-priority thread is created on each core
> > and uses low-level cpuidle functionalities to enter suspend, in all
> > the possible states and multiple times. This should allow a maximum
> > number of CPUs to enter the same sleep state at the same or slightly
> > different time.
>
> Since you're doing it multiple times, it might be useful to track the
> actual time it takes to suspend and wakeup, and possibly even warn
> if the time is longer than the target_residency. This could catch
> poorly configured idle states.
That's a good point, we can add a couple of checks for latencies on top
of the existing code, we will definitely do, at the moment we are
concerned with the overall design in particular in relation to usage of
high priority threads and tick broadcast to test the PSCI suspend
interface.
Thank you for having a look !
Lorenzo
^ permalink raw reply
* [PATCH 0/2] BQL support and fix for a regression issue
From: sunil.kovvuri at gmail.com @ 2016-09-22 9:05 UTC (permalink / raw)
To: linux-arm-kernel
From: Sunil Goutham <sgoutham@cavium.com>
These patches add byte queue limit support and also fixes a regression
issue introduced by commit
'net: thunderx: Use netdev's name for naming VF's interrupts'
Sunil Goutham (2):
net: thunderx: Fix issue with IRQ namimg
net: thunderx: Support for byte queue limits
drivers/net/ethernet/cavium/thunder/nic.h | 2 +-
drivers/net/ethernet/cavium/thunder/nicvf_main.c | 11 ++++++--
drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 30 ++++++++++++++--------
3 files changed, 30 insertions(+), 13 deletions(-)
--
2.7.4
^ permalink raw reply
* [PATCH 1/2] net: thunderx: Fix issue with IRQ namimg
From: sunil.kovvuri at gmail.com @ 2016-09-22 9:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474535121-13958-1-git-send-email-sunil.kovvuri@gmail.com>
From: Sunil Goutham <sgoutham@cavium.com>
This patch fixes a regression caused by previous commit
'Use netdev's name for naming VF's interrupts' where
irq name exceeds 20 byte array if interface's name size
is large.
Signed-off-by: Sunil Goutham <sgoutham@cavium.com>
---
drivers/net/ethernet/cavium/thunder/nic.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h
index 18d12d3..3042610 100644
--- a/drivers/net/ethernet/cavium/thunder/nic.h
+++ b/drivers/net/ethernet/cavium/thunder/nic.h
@@ -305,7 +305,7 @@ struct nicvf {
bool msix_enabled;
u8 num_vec;
struct msix_entry msix_entries[NIC_VF_MSIX_VECTORS];
- char irq_name[NIC_VF_MSIX_VECTORS][20];
+ char irq_name[NIC_VF_MSIX_VECTORS][IFNAMSIZ + 15];
bool irq_allocated[NIC_VF_MSIX_VECTORS];
cpumask_var_t affinity_mask[NIC_VF_MSIX_VECTORS];
--
2.7.4
^ permalink raw reply related
* [PATCH 2/2] net: thunderx: Support for byte queue limits
From: sunil.kovvuri at gmail.com @ 2016-09-22 9:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474535121-13958-1-git-send-email-sunil.kovvuri@gmail.com>
From: Sunil Goutham <sgoutham@cavium.com>
This patch adds support for byte queue limits
Signed-off-by: Sunil Goutham <sgoutham@cavium.com>
---
drivers/net/ethernet/cavium/thunder/nicvf_main.c | 11 ++++++--
drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 30 ++++++++++++++--------
2 files changed, 29 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index 7d00162..453e3a0 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -516,7 +516,8 @@ static int nicvf_init_resources(struct nicvf *nic)
static void nicvf_snd_pkt_handler(struct net_device *netdev,
struct cmp_queue *cq,
struct cqe_send_t *cqe_tx,
- int cqe_type, int budget)
+ int cqe_type, int budget,
+ unsigned int *tx_pkts, unsigned int *tx_bytes)
{
struct sk_buff *skb = NULL;
struct nicvf *nic = netdev_priv(netdev);
@@ -547,6 +548,8 @@ static void nicvf_snd_pkt_handler(struct net_device *netdev,
}
nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1);
prefetch(skb);
+ (*tx_pkts)++;
+ *tx_bytes += skb->len;
napi_consume_skb(skb, budget);
sq->skbuff[cqe_tx->sqe_ptr] = (u64)NULL;
} else {
@@ -662,6 +665,7 @@ static int nicvf_cq_intr_handler(struct net_device *netdev, u8 cq_idx,
struct cmp_queue *cq = &qs->cq[cq_idx];
struct cqe_rx_t *cq_desc;
struct netdev_queue *txq;
+ unsigned int tx_pkts = 0, tx_bytes = 0;
spin_lock_bh(&cq->lock);
loop:
@@ -701,7 +705,7 @@ loop:
case CQE_TYPE_SEND:
nicvf_snd_pkt_handler(netdev, cq,
(void *)cq_desc, CQE_TYPE_SEND,
- budget);
+ budget, &tx_pkts, &tx_bytes);
tx_done++;
break;
case CQE_TYPE_INVALID:
@@ -730,6 +734,9 @@ done:
netdev = nic->pnicvf->netdev;
txq = netdev_get_tx_queue(netdev,
nicvf_netdev_qidx(nic, cq_idx));
+ if (tx_pkts)
+ netdev_tx_completed_queue(txq, tx_pkts, tx_bytes);
+
nic = nic->pnicvf;
if (netif_tx_queue_stopped(txq) && netif_carrier_ok(netdev)) {
netif_tx_start_queue(txq);
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
index 178c5c7..a4fc501 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
@@ -1082,6 +1082,24 @@ static inline void nicvf_sq_add_cqe_subdesc(struct snd_queue *sq, int qentry,
imm->len = 1;
}
+static inline void nicvf_sq_doorbell(struct nicvf *nic, struct sk_buff *skb,
+ int sq_num, int desc_cnt)
+{
+ struct netdev_queue *txq;
+
+ txq = netdev_get_tx_queue(nic->pnicvf->netdev,
+ skb_get_queue_mapping(skb));
+
+ netdev_tx_sent_queue(txq, skb->len);
+
+ /* make sure all memory stores are done before ringing doorbell */
+ smp_wmb();
+
+ /* Inform HW to xmit all TSO segments */
+ nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_DOOR,
+ sq_num, desc_cnt);
+}
+
/* Segment a TSO packet into 'gso_size' segments and append
* them to SQ for transfer
*/
@@ -1141,12 +1159,8 @@ static int nicvf_sq_append_tso(struct nicvf *nic, struct snd_queue *sq,
/* Save SKB in the last segment for freeing */
sq->skbuff[hdr_qentry] = (u64)skb;
- /* make sure all memory stores are done before ringing doorbell */
- smp_wmb();
+ nicvf_sq_doorbell(nic, skb, sq_num, desc_cnt);
- /* Inform HW to xmit all TSO segments */
- nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_DOOR,
- sq_num, desc_cnt);
nic->drv_stats.tx_tso++;
return 1;
}
@@ -1219,12 +1233,8 @@ doorbell:
nicvf_sq_add_cqe_subdesc(sq, qentry, tso_sqe, skb);
}
- /* make sure all memory stores are done before ringing doorbell */
- smp_wmb();
+ nicvf_sq_doorbell(nic, skb, sq_num, subdesc_cnt);
- /* Inform HW to xmit new packet */
- nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_DOOR,
- sq_num, subdesc_cnt);
return 1;
append_fail:
--
2.7.4
^ permalink raw reply related
* [PATCHv2] arm64: Correctly bounds check virt_addr_valid
From: Will Deacon @ 2016-09-22 9:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474496704-30541-1-git-send-email-labbott@redhat.com>
On Wed, Sep 21, 2016 at 03:25:04PM -0700, Laura Abbott wrote:
>
> virt_addr_valid is supposed to return true if and only if virt_to_page
> returns a valid page structure. The current macro does math on whatever
> address is given and passes that to pfn_valid to verify. vmalloc and
> module addresses can happen to generate a pfn that 'happens' to be
> valid. Fix this by only performing the pfn_valid check on addresses that
> have the potential to be valid.
>
> Acked-by: Mark Rutland <mark.rutland@arm.com>
> Signed-off-by: Laura Abbott <labbott@redhat.com>
> ---
> v2: Properly parenthesize macro arguments. Re-factor to common macro.
>
> Also in case it wasn't clear, there's no need to try and squeeze this
> into 4.8. Hardened usercopy should have all the checks, this is just for
> full correctness.
Thanks, I'll push this onto for-next/core later today.
Will
^ permalink raw reply
* [PATCH] PCI: rockchip: fix uninitialized variable use
From: Arnd Bergmann @ 2016-09-22 9:39 UTC (permalink / raw)
To: linux-arm-kernel
The newly added pcie-rockchip driver fails to initialize the
io_size variable if the DT doesn't provide ranges for the PCI
I/O space, as found by building it with -Wmaybe-uninitialized:
drivers/pci/host/pcie-rockchip.c: In function 'rockchip_pcie_probe':
drivers/pci/host/pcie-rockchip.c:1007:6: warning: 'io_size' may be used uninitialized in this function [-Wmaybe-uninitialized]
This adds an appropriate initialization immediately in front of
the loop, so the io_size is zero as expected afterwards for that
case.
Fixes: abe17181b16f ("PCI: rockchip: Add Rockchip PCIe controller support")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
drivers/pci/host/pcie-rockchip.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
index c3593e633ccd..8bedc1e1ef80 100644
--- a/drivers/pci/host/pcie-rockchip.c
+++ b/drivers/pci/host/pcie-rockchip.c
@@ -1078,6 +1078,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
goto err_vpcie;
/* Get the I/O and memory ranges from DT */
+ io_size = 0;
resource_list_for_each_entry(win, &res) {
switch (resource_type(win->res)) {
case IORESOURCE_IO:
--
2.9.0
^ permalink raw reply related
* [RESEND PATCH v7 1/2] sdhci-of-arasan: Add device tree parameter xlnx,fails-without-test-cd bit
From: Ulf Hansson @ 2016-09-22 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474038102-20937-1-git-send-email-zach.brown@ni.com>
On 16 September 2016 at 17:01, Zach Brown <zach.brown@ni.com> wrote:
> The sdhci controller on xilinx zynq devices will not function unless
> the CD bit is provided. http://www.xilinx.com/support/answers/61064.html
> In cases where it is impossible to provide the CD bit in hardware,
> setting the controller to test mode and then setting inserted to true
> will get the controller to function without the CD bit.
>
> The device property "xlnx,fails-without-test-cd" will let the arasan
> driver know the controller does not have the CD line wired and that the
> controller does not function without it.
>
> Signed-off-by: Zach Brown <zach.brown@ni.com>
Thanks, applied for next! I took the liberty to shrink the commit msg
header bit and to add "dt: " prefix.
Kind regards
Uffe
> ---
> v2:
> * improved commit messages
> * removed fake-cd device property
> * removed fake-cd quirk
> * use broken-cd device property
> * documented new usage of broken-cd
> v3:
> * removed new usage of broken-cd
> * created fails-without-test-cd device property
> * created arasan controller specific quirk
> v4:
> * changed fails-without-test-cd to xlnx-fails-without-test-cd
> * removed extra blank line
> v5:
> * Fixed style mistake
> * Changed (1 << 0 ) to BIT(0)
> v6:
> * Fixed style mistakes
> * Condensed unnecessarily long variable names
> * Removed line wraps that were no longer necessary.
> * Rebased changes off Ulf's mmc tree's next branch.
> v7:
> * Removed erroneous re-creation of Optional Properties section
> * Changed xlnx-fails-without-test-cd to xlnx,fails-without-test-cd
> * Changed of_get_property to of_property_read_bool
>
>
> Documentation/devicetree/bindings/mmc/arasan,sdhci.txt | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
> index 3404afa..49df630 100644
> --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
> +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
> @@ -36,6 +36,9 @@ Optional Properties:
> - #clock-cells: If specified this should be the value <0>. With this property
> in place we will export a clock representing the Card Clock. This clock
> is expected to be consumed by our PHY. You must also specify
> + - xlnx,fails-without-test-cd: when present, the controller doesn't work when
> + the CD line is not connected properly, and the line is not connected
> + properly. Test mode can be used to force the controller to function.
>
> Example:
> sdhci at e0100000 {
> --
> 2.7.4
>
^ permalink raw reply
* [RESEND PATCH v7 2/2] sdhci-of-arasan: Set controller to test mode when xlnx,fails-without-test-cd is present
From: Ulf Hansson @ 2016-09-22 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1474038102-20937-2-git-send-email-zach.brown@ni.com>
On 16 September 2016 at 17:01, Zach Brown <zach.brown@ni.com> wrote:
> The sdhci controller on xilinx zynq devices will not function unless
> the CD bit is provided. http://www.xilinx.com/support/answers/61064.html
> In cases where it is impossible to provide the CD bit in hardware,
> setting the controller to test mode and then setting inserted to true
> will get the controller to function without the CD bit.
>
> When the device has the property xlnx,fails-without-test-cd the driver
> changes the controller to test mode and sets test inserted to true to
> make the controller function.
>
> Signed-off-by: Zach Brown <zach.brown@ni.com>
Thanks, applied for next! I took the liberty to shrink the commit msg
header bit and to add "mmc: " prefix.
Kind regards
Uffe
> ---
> drivers/mmc/host/sdhci-of-arasan.c | 27 ++++++++++++++++++++++++++-
> drivers/mmc/host/sdhci.h | 2 ++
> 2 files changed, 28 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
> index 33601a8..da8e40a 100644
> --- a/drivers/mmc/host/sdhci-of-arasan.c
> +++ b/drivers/mmc/host/sdhci-of-arasan.c
> @@ -26,6 +26,7 @@
> #include <linux/phy/phy.h>
> #include <linux/regmap.h>
> #include "sdhci-pltfm.h"
> +#include <linux/of.h>
>
> #define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c
> #define SDHCI_ARASAN_VENDOR_REGISTER 0x78
> @@ -98,6 +99,10 @@ struct sdhci_arasan_data {
>
> struct regmap *soc_ctl_base;
> const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
> + unsigned int quirks; /* Arasan deviations from spec */
> +
> +/* Controller does not have CD wired and will not function normally without */
> +#define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0)
> };
>
> static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = {
> @@ -245,12 +250,27 @@ static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc,
> writel(vendor, host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER);
> }
>
> +void sdhci_arasan_reset(struct sdhci_host *host, u8 mask)
> +{
> + u8 ctrl;
> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> + struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
> +
> + sdhci_reset(host, mask);
> +
> + if (sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) {
> + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
> + ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN;
> + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
> + }
> +}
> +
> static struct sdhci_ops sdhci_arasan_ops = {
> .set_clock = sdhci_arasan_set_clock,
> .get_max_clock = sdhci_pltfm_clk_get_max_clock,
> .get_timeout_clock = sdhci_arasan_get_timeout_clock,
> .set_bus_width = sdhci_set_bus_width,
> - .reset = sdhci_reset,
> + .reset = sdhci_arasan_reset,
> .set_uhs_signaling = sdhci_set_uhs_signaling,
> };
>
> @@ -545,6 +565,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
> struct sdhci_host *host;
> struct sdhci_pltfm_host *pltfm_host;
> struct sdhci_arasan_data *sdhci_arasan;
> + struct device_node *np = pdev->dev.of_node;
>
> host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata,
> sizeof(*sdhci_arasan));
> @@ -599,6 +620,10 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
> }
>
> sdhci_get_of_property(pdev);
> +
> + if (of_property_read_bool(np, "xlnx,fails-without-test-cd"))
> + sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_FORCE_CDTEST;
> +
> pltfm_host->clk = clk_xin;
>
> if (of_device_is_compatible(pdev->dev.of_node,
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index a2bc9e1..c722cd2 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -84,6 +84,8 @@
> #define SDHCI_CTRL_ADMA32 0x10
> #define SDHCI_CTRL_ADMA64 0x18
> #define SDHCI_CTRL_8BITBUS 0x20
> +#define SDHCI_CTRL_CDTEST_INS 0x40
> +#define SDHCI_CTRL_CDTEST_EN 0x80
>
> #define SDHCI_POWER_CONTROL 0x29
> #define SDHCI_POWER_ON 0x01
> --
> 2.7.4
>
^ permalink raw reply
* [PATCH] tty/serial: atmel: fix fractional baud rate computation
From: Boris Brezillon @ 2016-09-22 9:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160922093904.5b738ad6@bbrezillon>
On Thu, 22 Sep 2016 09:39:04 +0200
Boris Brezillon <boris.brezillon@free-electrons.com> wrote:
> On Thu, 22 Sep 2016 09:07:46 +0200
> Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de> wrote:
>
> > On Wed, Sep 21, 2016 at 12:44:14PM +0200, Nicolas Ferre wrote:
> > > From: Alexey Starikovskiy <aystarik@gmail.com>
> > >
> > > The problem with previous code was it rounded values in wrong
> > > place and produced wrong baud rate in some cases.
> > >
> > > Signed-off-by: Alexey Starikovskiy <aystarik@gmail.com>
> > > [nicolas.ferre at atmel.com: port to newer kernel and add commit log]
> > > Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
> > > ---
> > > drivers/tty/serial/atmel_serial.c | 10 ++++++----
> > > include/linux/atmel_serial.h | 1 +
> > > 2 files changed, 7 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
> > > index 5f550d9feed9..fd8aa1f4ba78 100644
> > > --- a/drivers/tty/serial/atmel_serial.c
> > > +++ b/drivers/tty/serial/atmel_serial.c
> > > @@ -2170,13 +2170,15 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
> > > * accurately. This feature is enabled only when using normal mode.
> > > * baudrate = selected clock / (8 * (2 - OVER) * (CD + FP / 8))
> > > * Currently, OVER is always set to 0 so we get
> > > - * baudrate = selected clock (16 * (CD + FP / 8))
> > > + * baudrate = selected clock / (16 * (CD + FP / 8))
> > > + * then
> > > + * 8 CD + FP = selected clock / (2 * baudrate)
> > > */
> > > if (atmel_port->has_frac_baudrate &&
> > > (mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_NORMAL) {
> > > - div = DIV_ROUND_CLOSEST(port->uartclk, baud);
> > > - cd = div / 16;
> > > - fp = DIV_ROUND_CLOSEST(div % 16, 2);
> > > + div = DIV_ROUND_CLOSEST(port->uartclk, baud * 2);
> > > + cd = div >> 3;
> > > + fp = div & ATMEL_US_FP_MASK;
> >
> > given baud = 115200 and uartclk = 5414300 this results in:
> >
> > div = DIV_ROUND_CLOSEST(5414300, 115200 * 2) = 23
> > cd = 2
> > fp = 7
>
> How about:
>
> div = DIV_ROUND_CLOSEST(port->uartclk, baud);
> cd = div / 16;
> fp = (div % 16) / 2;
>
> best_baud = port->uartclk / ((16 * cd) + (8 * fp));
>
> /* Check if we can get a better approximation by rounding up. */
> if (div % 2) {
> int alt_baud, alt_fp, alt_cd;
>
> alt_fp = fp++;
> alt_cd = cd;
> if (alt_fp > 7) {
> alt_cd++;
> alt_fp = 0;
> }
>
> alt_baud = port->uartclk / ((16 * alt_cd) + (8 *alt_fp));
> if (abs(best_baud - baud) > abs(alt_baud - baud)) {
After a lengthy discussion that happened on IRC (#armlinux), Uwe
proved me wrong. This should actually be
/*
* Calculate the Error in the time domain:
* Error = (RealBaudPeriod - ExpectedBaudPeriod) /
* ExpectedBaudPeriod;
*
* which after conversion to the frequency domain gives:
* Error = 1 - (ExpectedBaudRate/RealBaudRate);
*
* and since we want to compare 2 errors and avoid
* approximation, we have:
*
* if (RealBaudRate2 * (RealBaudRate1 - ExpectedBaudRate) <
* RealBaudRate1 * (RealBaudRate2 - ExpectedBaudRate))
* ...
*
*/
if (alt_baud * abs(best_baud - baud) >
best_baud * abs(alt_baud - baud))
Thanks for your patience ;-).
> best_baud = alt_baud;
> fp = alt_fp;
> cd = alt_cd;
> }
> }
>
> >
> > which yields a rate of 5414300 / 46 = 117702.17. With cd = 3 and fp = 0
> > however the resulting rate is 5414300 / 48 = 112797.92.
> >
> > Which one is better?
> >
> > > } else {
> > > cd = uart_get_divisor(port, baud);
> > > }
> > > diff --git a/include/linux/atmel_serial.h b/include/linux/atmel_serial.h
> > > index f8e452aa48d7..bd2560502f3c 100644
> > > --- a/include/linux/atmel_serial.h
> > > +++ b/include/linux/atmel_serial.h
> > > @@ -119,6 +119,7 @@
> > > #define ATMEL_US_BRGR 0x20 /* Baud Rate Generator Register */
> > > #define ATMEL_US_CD GENMASK(15, 0) /* Clock Divider */
> > > #define ATMEL_US_FP_OFFSET 16 /* Fractional Part */
> > > +#define ATMEL_US_FP_MASK 0x7
> >
> > Is there another user of this header? If not, this can be folded into
> > the driver.
> >
> > Best regards
> > Uwe
> >
>
^ permalink raw reply
* [PATCH v2 2/3] devicetree: bindings: uart: Add new compatible string for ZynqMP
From: Greg KH @ 2016-09-22 9:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473930931-1034-2-git-send-email-navam@xilinx.com>
On Thu, Sep 15, 2016 at 02:45:30PM +0530, Nava kishore Manne wrote:
> From: Nava kishore Manne <nava.manne@xilinx.com>
>
> Signed-off-by: Nava kishore Manne <navam@xilinx.com>
> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
> [stelford at cadence.com: cherry picked from
> https://github.com/Xilinx/linux-xlnx commit
> 37b8a9780766422b2437f5166ddef3767bb60887]
> Signed-off-by: Scott Telford <stelford@cadence.com>
I can't take patches without any changelog comment at all, sorry.
greg k-h
^ permalink raw reply
* [PATCHv2 2/3] tty/serial: at91: fix hardware handshake with GPIOs
From: Greg Kroah-Hartman @ 2016-09-22 9:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160912094733.21501-3-richard.genoud@gmail.com>
On Mon, Sep 12, 2016 at 11:47:32AM +0200, Richard Genoud wrote:
> Commit 1cf6e8fc8341 ("tty/serial: at91: fix RTS line management when
> hardware handshake is enabled") broke the hardware handshake when GPIOs
> where used.
>
> Hardware handshake with GPIOs used to work before this commit because
> the CRTSCTS flag (termios->c_cflag) was set, but not the
> ATMEL_US_USMODE_HWHS flag (controller register) ; so hardware handshake
> enabled, but not handled by the controller.
>
> This commit restores this behaviour.
>
> NB: -stable is not Cced because it doesn't cleanly apply on 4.1+
> and it will also need previous commit:
> "serial: mctrl_gpio: implement mctrl_gpio_use_rtscts"
>
> Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
> Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
> Fixes: 1cf6e8fc8341 ("tty/serial: at91: fix RTS line management when hardware handshake is enabled")
> ---
> drivers/tty/serial/atmel_serial.c | 11 ++++++++---
> 1 file changed, 8 insertions(+), 3 deletions(-)
This patch doesn't apply to my tree, are you sure it's up to date?
Can you please refresh this whole series against linux-next and resend?
thanks,
greg k-h
^ 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