* [PATCH v2 0/6] mm: fix the "counter.sh" failure for libhugetlbfs
From: Vlastimil Babka @ 2016-11-28 14:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479107259-2011-1-git-send-email-shijie.huang@arm.com>
On 11/14/2016 08:07 AM, Huang Shijie wrote:
> (1) Background
> For the arm64, the hugetlb page size can be 32M (PMD + Contiguous bit).
> In the 4K page environment, the max page order is 10 (max_order - 1),
> so 32M page is the gigantic page.
>
> The arm64 MMU supports a Contiguous bit which is a hint that the TTE
> is one of a set of contiguous entries which can be cached in a single
> TLB entry. Please refer to the arm64v8 mannul :
> DDI0487A_f_armv8_arm.pdf (in page D4-1811)
>
> (2) The bug
> After I tested the libhugetlbfs, I found the test case "counter.sh"
> will fail with the gigantic page (32M page in arm64 board).
>
> This patch set adds support for gigantic surplus hugetlb pages,
> allowing the counter.sh unit test to pass.
>
> v1 -- > v2:
> 1.) fix the compiler error in X86.
> 2.) add new patches for NUMA.
> The patch #2 ~ #5 are new patches.
>
> Huang Shijie (6):
> mm: hugetlb: rename some allocation functions
> mm: hugetlb: add a new parameter for some functions
> mm: hugetlb: change the return type for alloc_fresh_gigantic_page
> mm: mempolicy: intruduce a helper huge_nodemask()
> mm: hugetlb: add a new function to allocate a new gigantic page
> mm: hugetlb: support gigantic surplus pages
>
> include/linux/mempolicy.h | 8 +++
> mm/hugetlb.c | 128 ++++++++++++++++++++++++++++++++++++----------
> mm/mempolicy.c | 20 ++++++++
> 3 files changed, 130 insertions(+), 26 deletions(-)
Can't say I'm entirely happy with the continued direction of maze of
functions for huge page allocation :( Feels like path of least
resistance to basically copy/paste the missing parts here. Is there no
way to consolidate the code more?
^ permalink raw reply
* [PATCH v7 1/8] drm: sun8i: Add a basic DRM driver for Allwinner DE2
From: Jean-Francois Moine @ 2016-11-28 14:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1480414715.git.moinejf@free.fr>
Allwinner's recent SoCs, as A64, A83T and H3, contain a new display
engine, DE2.
This patch adds a DRM video driver for this device.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/sun8i/Kconfig | 19 +
drivers/gpu/drm/sun8i/Makefile | 7 +
drivers/gpu/drm/sun8i/de2_crtc.c | 449 +++++++++++++++++++++++
drivers/gpu/drm/sun8i/de2_crtc.h | 50 +++
drivers/gpu/drm/sun8i/de2_drv.c | 317 ++++++++++++++++
drivers/gpu/drm/sun8i/de2_drv.h | 48 +++
drivers/gpu/drm/sun8i/de2_plane.c | 734 ++++++++++++++++++++++++++++++++++++++
9 files changed, 1627 insertions(+)
create mode 100644 drivers/gpu/drm/sun8i/Kconfig
create mode 100644 drivers/gpu/drm/sun8i/Makefile
create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c
create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h
create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c
create mode 100644 drivers/gpu/drm/sun8i/de2_drv.h
create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 95fc041..bb1bfbc 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -202,6 +202,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
source "drivers/gpu/drm/sun4i/Kconfig"
+source "drivers/gpu/drm/sun8i/Kconfig"
+
source "drivers/gpu/drm/omapdrm/Kconfig"
source "drivers/gpu/drm/tilcdc/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 883f3e7..3e1eaa0 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
obj-y += omapdrm/
obj-$(CONFIG_DRM_SUN4I) += sun4i/
+obj-$(CONFIG_DRM_SUN8I) += sun8i/
obj-y += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_BOCHS) += bochs/
diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
new file mode 100644
index 0000000..6940895
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/Kconfig
@@ -0,0 +1,19 @@
+#
+# Allwinner DE2 Video configuration
+#
+
+config DRM_SUN8I
+ bool
+
+config DRM_SUN8I_DE2
+ tristate "Support for Allwinner Video with DE2 interface"
+ depends on DRM && OF
+ depends on ARCH_SUNXI || COMPILE_TEST
+ select DRM_GEM_CMA_HELPER
+ select DRM_KMS_CMA_HELPER
+ select DRM_KMS_HELPER
+ select DRM_SUN8I
+ help
+ Choose this option if your Allwinner chipset has the DE2 interface
+ as the A64, A83T and H3. If M is selected the module will be called
+ sun8i-de2-drm.
diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
new file mode 100644
index 0000000..f107919
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Allwinner's sun8i DRM device driver
+#
+
+sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
+
+obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
diff --git a/drivers/gpu/drm/sun8i/de2_crtc.c b/drivers/gpu/drm/sun8i/de2_crtc.c
new file mode 100644
index 0000000..4e94ccc
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_crtc.c
@@ -0,0 +1,449 @@
+/*
+ * Allwinner DRM driver - DE2 CRTC
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/component.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <linux/io.h>
+#include <linux/of_irq.h>
+#include <linux/of_graph.h>
+
+#include "de2_drv.h"
+#include "de2_crtc.h"
+
+/* I/O map */
+
+#define TCON_GCTL_REG 0x00
+#define TCON_GCTL_TCON_ENABLE BIT(31)
+#define TCON_GINT0_REG 0x04
+#define TCON_GINT0_TCON1_Vb_Int_En BIT(30)
+#define TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)
+#define TCON_GINT0_TCON1_Vb_Line_Int_Flag BIT(12)
+#define TCON0_CTL_REG 0x40
+#define TCON0_CTL_TCON_ENABLE BIT(31)
+#define TCON1_CTL_REG 0x90
+#define TCON1_CTL_TCON_ENABLE BIT(31)
+#define TCON1_CTL_INTERLACE_ENABLE BIT(20)
+#define TCON1_CTL_Start_Delay_SHIFT 4
+#define TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)
+#define TCON1_BASIC0_REG 0x94 /* XI/YI */
+#define TCON1_BASIC1_REG 0x98 /* LS_XO/LS_YO */
+#define TCON1_BASIC2_REG 0x9c /* XO/YO */
+#define TCON1_BASIC3_REG 0xa0 /* HT/HBP */
+#define TCON1_BASIC4_REG 0xa4 /* VT/VBP */
+#define TCON1_BASIC5_REG 0xa8 /* HSPW/VSPW */
+#define TCON1_PS_SYNC_REG 0xb0
+#define TCON1_IO_POL_REG 0xf0
+#define TCON1_IO_POL_IO0_inv BIT(24)
+#define TCON1_IO_POL_IO1_inv BIT(25)
+#define TCON1_IO_POL_IO2_inv BIT(26)
+#define TCON1_IO_TRI_REG 0xf4
+#define TCON_CEU_CTL_REG 0x100
+#define TCON_CEU_CTL_ceu_en BIT(31)
+#define TCON1_FILL_CTL_REG 0x300
+#define TCON1_FILL_START0_REG 0x304
+#define TCON1_FILL_END0_REG 0x308
+#define TCON1_FILL_DATA0_REG 0x30c
+
+#define XY(x, y) (((x) << 16) | (y))
+
+#define andl_relaxed(addr, val) \
+ writel_relaxed(readl_relaxed(addr) & val, addr)
+#define orl_relaxed(addr, val) \
+ writel_relaxed(readl_relaxed(addr) | val, addr)
+
+/* vertical blank functions */
+
+static void de2_atomic_flush(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct drm_pending_vblank_event *event = crtc->state->event;
+
+ if (event) {
+ crtc->state->event = NULL;
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (drm_crtc_vblank_get(crtc) == 0)
+ drm_crtc_arm_vblank_event(crtc, event);
+ else
+ drm_crtc_send_vblank_event(crtc, event);
+ spin_unlock_irq(&crtc->dev->event_lock);
+ }
+}
+
+static irqreturn_t de2_lcd_irq(int irq, void *dev_id)
+{
+ struct lcd *lcd = (struct lcd *) dev_id;
+ u32 isr;
+
+ isr = readl_relaxed(lcd->mmio + TCON_GINT0_REG);
+
+ drm_crtc_handle_vblank(&lcd->crtc);
+
+ writel_relaxed(isr &
+ ~(TCON_GINT0_TCON1_Vb_Int_Flag |
+ TCON_GINT0_TCON1_Vb_Line_Int_Flag),
+ lcd->mmio + TCON_GINT0_REG);
+
+ return IRQ_HANDLED;
+}
+
+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc_ix)
+{
+ struct priv *priv = drm_to_priv(drm);
+ struct lcd *lcd = priv->lcds[crtc_ix];
+
+ orl_relaxed(lcd->mmio + TCON_GINT0_REG, TCON_GINT0_TCON1_Vb_Int_En);
+
+ return 0;
+}
+
+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc_ix)
+{
+ struct priv *priv = drm_to_priv(drm);
+ struct lcd *lcd = priv->lcds[crtc_ix];
+
+ andl_relaxed(lcd->mmio + TCON_GINT0_REG, ~TCON_GINT0_TCON1_Vb_Int_En);
+}
+
+void de2_vblank_reset(struct lcd *lcd)
+{
+ drm_crtc_vblank_reset(&lcd->crtc);
+}
+
+/* frame functions */
+static int de2_crtc_set_clock(struct lcd *lcd, int rate)
+{
+ struct clk *parent_clk;
+ u32 parent_rate;
+ int ret;
+
+ /* determine and set the best rate for the parent clock (pll-video) */
+ if ((270000 * 2) % rate == 0)
+ parent_rate = 270000000;
+ else if (297000 % rate == 0)
+ parent_rate = 297000000;
+ else
+ return -EINVAL; /* unsupported clock */
+
+ parent_clk = clk_get_parent(lcd->clk);
+
+ ret = clk_set_rate(parent_clk, parent_rate);
+ if (ret) {
+ dev_err(lcd->dev, "set parent rate failed %d\n", ret);
+ return ret;
+ }
+ ret = clk_set_rate(lcd->clk, rate * 1000);
+ if (ret) {
+ dev_err(lcd->dev, "set rate failed %d\n", ret);
+ return ret;
+ }
+
+ /* enable the clock */
+ reset_control_deassert(lcd->reset);
+ clk_prepare_enable(lcd->bus);
+ clk_prepare_enable(lcd->clk);
+
+ return ret;
+}
+
+static void de2_tcon_init(struct lcd *lcd)
+{
+ andl_relaxed(lcd->mmio + TCON0_CTL_REG, ~TCON0_CTL_TCON_ENABLE);
+ andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
+ andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
+
+ /* disable/ack interrupts */
+ writel_relaxed(0, lcd->mmio + TCON_GINT0_REG);
+}
+
+static void de2_tcon_enable(struct lcd *lcd)
+{
+ struct drm_crtc *crtc = &lcd->crtc;
+ const struct drm_display_mode *mode = &crtc->mode;
+ int interlace = mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 1;
+ int start_delay;
+ u32 data;
+
+ orl_relaxed(lcd->mmio + TCON_GCTL_REG, TCON_GCTL_TCON_ENABLE);
+
+ data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);
+ writel_relaxed(data, lcd->mmio + TCON1_BASIC0_REG);
+ writel_relaxed(data, lcd->mmio + TCON1_BASIC1_REG);
+ writel_relaxed(data, lcd->mmio + TCON1_BASIC2_REG);
+ writel_relaxed(XY(mode->htotal - 1,
+ mode->htotal - mode->hsync_start - 1),
+ lcd->mmio + TCON1_BASIC3_REG);
+ writel_relaxed(XY(mode->vtotal * (3 - interlace),
+ mode->vtotal - mode->vsync_start - 1),
+ lcd->mmio + TCON1_BASIC4_REG);
+ writel_relaxed(XY(mode->hsync_end - mode->hsync_start - 1,
+ mode->vsync_end - mode->vsync_start - 1),
+ lcd->mmio + TCON1_BASIC5_REG);
+
+ data = TCON1_IO_POL_IO2_inv;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ data |= TCON1_IO_POL_IO0_inv;
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ data |= TCON1_IO_POL_IO1_inv;
+ writel_relaxed(data, lcd->mmio + TCON1_IO_POL_REG);
+
+ andl_relaxed(lcd->mmio + TCON_CEU_CTL_REG, ~TCON_CEU_CTL_ceu_en);
+
+ if (interlace == 2)
+ orl_relaxed(lcd->mmio + TCON1_CTL_REG,
+ TCON1_CTL_INTERLACE_ENABLE);
+ else
+ andl_relaxed(lcd->mmio + TCON1_CTL_REG,
+ ~TCON1_CTL_INTERLACE_ENABLE);
+
+ writel_relaxed(0, lcd->mmio + TCON1_FILL_CTL_REG);
+ writel_relaxed(mode->vtotal + 1, lcd->mmio + TCON1_FILL_START0_REG);
+ writel_relaxed(mode->vtotal, lcd->mmio + TCON1_FILL_END0_REG);
+ writel_relaxed(0, lcd->mmio + TCON1_FILL_DATA0_REG);
+
+ start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;
+ if (start_delay > 31)
+ start_delay = 31;
+ data = readl_relaxed(lcd->mmio + TCON1_CTL_REG);
+ data &= ~TCON1_CTL_Start_Delay_MASK;
+ data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;
+ writel_relaxed(data, lcd->mmio + TCON1_CTL_REG);
+
+ orl_relaxed(lcd->mmio + TCON1_CTL_REG, TCON1_CTL_TCON_ENABLE);
+}
+
+static void de2_tcon_disable(struct lcd *lcd)
+{
+ andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
+ andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
+}
+
+static void de2_crtc_enable(struct drm_crtc *crtc)
+{
+ struct lcd *lcd = crtc_to_lcd(crtc);
+ struct drm_display_mode *mode = &crtc->mode;
+
+ if (de2_crtc_set_clock(lcd, mode->clock) < 0)
+ return;
+ lcd->clk_enabled = true;
+
+ /* start the TCON and the DE */
+ de2_tcon_enable(lcd);
+ de2_de_enable(lcd);
+
+ /* turn on blanking interrupt */
+ drm_crtc_vblank_on(crtc);
+}
+
+static void de2_crtc_disable(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
+{
+ struct lcd *lcd = crtc_to_lcd(crtc);
+
+ if (!lcd->clk_enabled)
+ return; /* already disabled */
+ lcd->clk_enabled = false;
+
+ de2_de_disable(lcd);
+
+ drm_crtc_vblank_off(crtc);
+
+ de2_tcon_disable(lcd);
+
+ clk_disable_unprepare(lcd->clk);
+ clk_disable_unprepare(lcd->bus);
+ reset_control_assert(lcd->reset);
+}
+
+static const struct drm_crtc_funcs de2_crtc_funcs = {
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_crtc_helper_funcs de2_crtc_helper_funcs = {
+ .atomic_flush = de2_atomic_flush,
+ .enable = de2_crtc_enable,
+ .atomic_disable = de2_crtc_disable,
+};
+
+/* device init */
+static int de2_lcd_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct drm_device *drm = data;
+ struct priv *priv = drm_to_priv(drm);
+ struct lcd *lcd = dev_get_drvdata(dev);
+ struct drm_crtc *crtc = &lcd->crtc;
+ int ret, i, crtc_ix;
+
+ lcd->priv = priv;
+
+ /* set the CRTC reference */
+ crtc_ix = drm_crtc_index(crtc);
+ if (crtc_ix >= ARRAY_SIZE(priv->lcds)) {
+ dev_err(drm->dev, "Bad crtc index");
+ return -ENOENT;
+ }
+ priv->lcds[crtc_ix] = lcd;
+
+ /* and the mixer index (DT port index in the DE) */
+ for (i = 0; ; i++) {
+ struct device_node *port;
+
+ port = of_parse_phandle(drm->dev->of_node, "ports", i);
+ if (!port)
+ break;
+ if (port == lcd->crtc.port) {
+ lcd->mixer = i;
+ break;
+ }
+ }
+
+ ret = de2_plane_init(drm, lcd);
+ if (ret < 0)
+ return ret;
+
+ drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);
+
+ return drm_crtc_init_with_planes(drm, crtc,
+ &lcd->planes[DE2_PRIMARY_PLANE],
+ &lcd->planes[DE2_CURSOR_PLANE],
+ &de2_crtc_funcs, NULL);
+}
+
+static void de2_lcd_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lcd *lcd = platform_get_drvdata(pdev);
+
+ if (lcd->priv)
+ lcd->priv->lcds[drm_crtc_index(&lcd->crtc)] = NULL;
+}
+
+static const struct component_ops de2_lcd_ops = {
+ .bind = de2_lcd_bind,
+ .unbind = de2_lcd_unbind,
+};
+
+static int de2_lcd_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node, *tmp, *parent, *port;
+ struct lcd *lcd;
+ struct resource *res;
+ int id, irq, ret;
+
+ lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, lcd);
+ lcd->dev = dev;
+ lcd->mixer = id;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get memory resource\n");
+ return -EINVAL;
+ }
+
+ lcd->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(lcd->mmio)) {
+ dev_err(dev, "failed to map registers\n");
+ return PTR_ERR(lcd->mmio);
+ }
+
+ /* possible CRTC */
+ parent = np;
+ tmp = of_get_child_by_name(np, "ports");
+ if (tmp)
+ parent = tmp;
+ port = of_get_child_by_name(parent, "port");
+ of_node_put(tmp);
+ if (!port) {
+ dev_err(dev, "no port node\n");
+ return -ENXIO;
+ }
+ lcd->crtc.port = port;
+
+ lcd->bus = devm_clk_get(dev, "bus");
+ if (IS_ERR(lcd->bus)) {
+ dev_err(dev, "get bus clock err %d\n", (int) PTR_ERR(lcd->bus));
+ ret = PTR_ERR(lcd->bus);
+ goto err;
+ }
+
+ lcd->clk = devm_clk_get(dev, "clock");
+ if (IS_ERR(lcd->clk)) {
+ ret = PTR_ERR(lcd->clk);
+ dev_err(dev, "get video clock err %d\n", ret);
+ goto err;
+ }
+
+ lcd->reset = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(lcd->reset)) {
+ ret = PTR_ERR(lcd->reset);
+ dev_err(dev, "get reset err %d\n", ret);
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(dev, "unable to get irq\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ de2_tcon_init(lcd); /* stop TCON and avoid interrupts */
+
+ ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,
+ dev_name(dev), lcd);
+ if (ret < 0) {
+ dev_err(dev, "unable to request irq %d\n", irq);
+ goto err;
+ }
+
+ return component_add(dev, &de2_lcd_ops);
+
+err:
+ of_node_put(lcd->crtc.port);
+ return ret;
+}
+
+static int de2_lcd_remove(struct platform_device *pdev)
+{
+ struct lcd *lcd = platform_get_drvdata(pdev);
+
+ component_del(&pdev->dev, &de2_lcd_ops);
+
+ of_node_put(lcd->crtc.port);
+
+ return 0;
+}
+
+static const struct of_device_id de2_lcd_ids[] = {
+ { .compatible = "allwinner,sun8i-a83t-tcon", },
+ { }
+};
+
+struct platform_driver de2_lcd_platform_driver = {
+ .probe = de2_lcd_probe,
+ .remove = de2_lcd_remove,
+ .driver = {
+ .name = "sun8i-de2-tcon",
+ .of_match_table = of_match_ptr(de2_lcd_ids),
+ },
+};
diff --git a/drivers/gpu/drm/sun8i/de2_crtc.h b/drivers/gpu/drm/sun8i/de2_crtc.h
new file mode 100644
index 0000000..c0d34a7
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_crtc.h
@@ -0,0 +1,50 @@
+#ifndef __DE2_CRTC_H__
+#define __DE2_CRTC_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drm_plane_helper.h>
+
+struct clk;
+struct reset_control;
+struct priv;
+
+/* planes */
+#define DE2_PRIMARY_PLANE 0
+#define DE2_CURSOR_PLANE 1
+#define DE2_N_PLANES 5 /* number of planes - see plane_tb[] in de2_plane.c */
+
+struct lcd {
+ void __iomem *mmio;
+
+ struct device *dev;
+ struct drm_crtc crtc;
+
+ struct priv *priv; /* DRM/DE private data */
+
+ u8 mixer; /* LCD (mixer) number */
+ u8 delayed; /* bitmap of planes with delayed update */
+
+ u8 clk_enabled; /* used for error in crtc_enable */
+
+ struct clk *clk;
+ struct clk *bus;
+ struct reset_control *reset;
+
+ struct drm_plane planes[DE2_N_PLANES];
+};
+
+#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)
+
+/* in de2_plane.c */
+void de2_de_enable(struct lcd *lcd);
+void de2_de_disable(struct lcd *lcd);
+int de2_plane_init(struct drm_device *drm, struct lcd *lcd);
+
+#endif /* __DE2_CRTC_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_drv.c b/drivers/gpu/drm/sun8i/de2_drv.c
new file mode 100644
index 0000000..f96babe
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_drv.c
@@ -0,0 +1,317 @@
+/*
+ * Allwinner DRM driver - DE2 DRM driver
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <drm/drm_of.h>
+#include <linux/component.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "de2_drv.h"
+
+#define DRIVER_NAME "sun8i-de2"
+#define DRIVER_DESC "Allwinner DRM DE2"
+#define DRIVER_DATE "20161101"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+static const struct of_device_id de2_drm_of_match[] = {
+ { .compatible = "allwinner,sun8i-a83t-display-engine",
+ .data = (void *) SOC_A83T },
+ { .compatible = "allwinner,sun8i-h3-display-engine",
+ .data = (void *) SOC_H3 },
+ { },
+};
+MODULE_DEVICE_TABLE(of, de2_drm_of_match);
+
+static void de2_fb_output_poll_changed(struct drm_device *drm)
+{
+ struct priv *priv = drm_to_priv(drm);
+
+ if (priv->fbdev)
+ drm_fbdev_cma_hotplug_event(priv->fbdev);
+}
+
+static const struct drm_mode_config_funcs de2_mode_config_funcs = {
+ .fb_create = drm_fb_cma_create,
+ .output_poll_changed = de2_fb_output_poll_changed,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+/* -- DRM operations -- */
+
+static void de2_lastclose(struct drm_device *drm)
+{
+ struct priv *priv = drm_to_priv(drm);
+
+ if (priv->fbdev)
+ drm_fbdev_cma_restore_mode(priv->fbdev);
+}
+
+static const struct file_operations de2_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .poll = drm_poll,
+ .read = drm_read,
+ .llseek = no_llseek,
+ .mmap = drm_gem_cma_mmap,
+};
+
+static struct drm_driver de2_drm_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
+ DRIVER_ATOMIC,
+ .lastclose = de2_lastclose,
+ .get_vblank_counter = drm_vblank_no_hw_counter,
+ .enable_vblank = de2_enable_vblank,
+ .disable_vblank = de2_disable_vblank,
+ .gem_free_object = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_vmap = drm_gem_cma_prime_vmap,
+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+ .gem_prime_mmap = drm_gem_cma_prime_mmap,
+ .dumb_create = drm_gem_cma_dumb_create,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .fops = &de2_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+/*
+ * Platform driver
+ */
+
+static int de2_drm_bind(struct device *dev)
+{
+ struct drm_device *drm;
+ struct priv *priv;
+ struct resource *res;
+ struct lcd *lcd;
+ int i, ret;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ drm = &priv->drm;
+ dev_set_drvdata(dev, drm);
+
+ /* get the resources */
+ priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;
+
+ res = platform_get_resource(to_platform_device(dev),
+ IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get memory resource\n");
+ ret = -EINVAL;
+ goto out1;
+ }
+
+ priv->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->mmio)) {
+ ret = PTR_ERR(priv->mmio);
+ dev_err(dev, "failed to map registers %d\n", ret);
+ goto out1;
+ }
+
+ priv->gate = devm_clk_get(dev, "bus");
+ if (IS_ERR(priv->gate)) {
+ ret = PTR_ERR(priv->gate);
+ dev_err(dev, "bus gate err %d\n", ret);
+ goto out1;
+ }
+
+ priv->clk = devm_clk_get(dev, "clock");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_err(dev, "clock err %d\n", ret);
+ goto out1;
+ }
+
+ priv->reset = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(priv->reset)) {
+ ret = PTR_ERR(priv->reset);
+ dev_err(dev, "reset err %d\n", ret);
+ goto out1;
+ }
+
+ mutex_init(&priv->mutex); /* protect DE I/O accesses */
+
+ ret = drm_dev_init(drm, &de2_drm_driver, dev);
+ if (ret != 0) {
+ dev_err(dev, "dev_init failed %d\n", ret);
+ goto out1;
+ }
+
+ drm_mode_config_init(drm);
+ drm->mode_config.min_width = 32; /* needed for cursor */
+ drm->mode_config.min_height = 32;
+ drm->mode_config.max_width = 1920;
+ drm->mode_config.max_height = 1080;
+ drm->mode_config.funcs = &de2_mode_config_funcs;
+
+ drm->irq_enabled = true;
+
+ /* start the subdevices */
+ ret = component_bind_all(dev, drm);
+ if (ret < 0)
+ goto out2;
+
+ /* initialize and disable vertical blanking on all CRTCs */
+ ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+ if (ret < 0)
+ dev_warn(dev, "vblank_init failed %d\n", ret);
+
+ for (i = 0; i < ARRAY_SIZE(priv->lcds); i++) {
+ lcd = priv->lcds[i];
+ if (lcd)
+ de2_vblank_reset(lcd);
+ }
+
+ drm_mode_config_reset(drm);
+
+ priv->fbdev = drm_fbdev_cma_init(drm,
+ 32, /* bpp */
+ drm->mode_config.num_crtc,
+ drm->mode_config.num_connector);
+ if (IS_ERR(priv->fbdev)) {
+ ret = PTR_ERR(priv->fbdev);
+ priv->fbdev = NULL;
+ goto out3;
+ }
+
+ drm_kms_helper_poll_init(drm);
+
+ ret = drm_dev_register(drm, 0);
+ if (ret < 0)
+ goto out4;
+
+ return 0;
+
+out4:
+ drm_fbdev_cma_fini(priv->fbdev);
+out3:
+ component_unbind_all(dev, drm);
+out2:
+ drm_dev_unref(drm);
+out1:
+ kfree(priv);
+ return ret;
+}
+
+static void de2_drm_unbind(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct priv *priv = drm_to_priv(drm);
+
+ drm_dev_unregister(drm);
+
+ drm_fbdev_cma_fini(priv->fbdev);
+ drm_kms_helper_poll_fini(drm);
+ drm_vblank_cleanup(drm);
+ drm_mode_config_cleanup(drm);
+
+ component_unbind_all(dev, drm);
+
+ kfree(priv);
+}
+
+static const struct component_master_ops de2_drm_comp_ops = {
+ .bind = de2_drm_bind,
+ .unbind = de2_drm_unbind,
+};
+
+/*
+ * drm_of_component_probe() does:
+ * - bind of the ports (lcd-controller.port)
+ * - bind of the remote nodes (hdmi, tve..)
+ */
+static int compare_of(struct device *dev, void *data)
+{
+ struct device_node *np = data;
+
+ if (of_node_cmp(np->name, "port") == 0) {
+ np = of_get_parent(np);
+ of_node_put(np);
+ }
+ return dev->of_node == np;
+}
+
+static int de2_drm_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = drm_of_component_probe(&pdev->dev,
+ compare_of,
+ &de2_drm_comp_ops);
+ if (ret == -EINVAL)
+ ret = -ENXIO;
+ return ret;
+}
+
+static int de2_drm_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &de2_drm_comp_ops);
+
+ return 0;
+}
+
+static struct platform_driver de2_drm_platform_driver = {
+ .probe = de2_drm_probe,
+ .remove = de2_drm_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = de2_drm_of_match,
+ },
+};
+
+static int __init de2_drm_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&de2_lcd_platform_driver);
+ if (ret < 0)
+ return ret;
+
+ ret = platform_driver_register(&de2_drm_platform_driver);
+ if (ret < 0)
+ platform_driver_unregister(&de2_lcd_platform_driver);
+
+ return ret;
+}
+
+static void __exit de2_drm_fini(void)
+{
+ platform_driver_unregister(&de2_lcd_platform_driver);
+ platform_driver_unregister(&de2_drm_platform_driver);
+}
+
+module_init(de2_drm_init);
+module_exit(de2_drm_fini);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("Allwinner DE2 DRM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sun8i/de2_drv.h b/drivers/gpu/drm/sun8i/de2_drv.h
new file mode 100644
index 0000000..c42c30a
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_drv.h
@@ -0,0 +1,48 @@
+#ifndef __DE2_DRM_H__
+#define __DE2_DRM_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+struct drm_fbdev_cma;
+struct lcd;
+
+#define N_LCDS 2
+
+struct priv {
+ struct drm_device drm;
+ void __iomem *mmio;
+ struct clk *clk;
+ struct clk *gate;
+ struct reset_control *reset;
+
+ struct mutex mutex; /* protect DE I/O access */
+ u8 soc_type;
+#define SOC_A83T 0
+#define SOC_H3 1
+ u8 started; /* bitmap of started mixers */
+ u8 clean; /* bitmap of clean mixers */
+
+ struct drm_fbdev_cma *fbdev;
+
+ struct lcd *lcds[N_LCDS]; /* CRTCs */
+};
+
+#define drm_to_priv(x) container_of(x, struct priv, drm)
+
+/* in de2_crtc.c */
+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc);
+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc);
+void de2_vblank_reset(struct lcd *lcd);
+extern struct platform_driver de2_lcd_platform_driver;
+
+#endif /* __DE2_DRM_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_plane.c b/drivers/gpu/drm/sun8i/de2_plane.c
new file mode 100644
index 0000000..2fd72dc
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_plane.c
@@ -0,0 +1,734 @@
+/*
+ * Allwinner DRM driver - Display Engine 2
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
+ * Copyright (c) 2016 Allwinnertech Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/io.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "de2_drv.h"
+#include "de2_crtc.h"
+
+/* DE2 I/O map */
+
+#define DE2_MOD_REG 0x0000 /* 1 bit per LCD */
+#define DE2_GATE_REG 0x0004
+#define DE2_RESET_REG 0x0008
+#define DE2_DIV_REG 0x000c /* 4 bits per LCD */
+#define DE2_SEL_REG 0x0010
+
+#define DE2_MIXER0_BASE 0x00100000 /* LCD 0 */
+#define DE2_MIXER1_BASE 0x00200000 /* LCD 1 */
+
+/* mixer registers (addr / mixer base) */
+#define MIXER_GLB_REGS 0x00000 /* global control */
+#define MIXER_BLD_REGS 0x01000 /* alpha blending */
+#define MIXER_CHAN_REGS 0x02000 /* VI/UI overlay channels */
+#define MIXER_CHAN_SZ 0x1000 /* size of a channel */
+#define MIXER_VSU_REGS 0x20000 /* VSU */
+#define MIXER_GSU1_REGS 0x30000 /* GSUs */
+#define MIXER_GSU2_REGS 0x40000
+#define MIXER_GSU3_REGS 0x50000
+#define MIXER_FCE_REGS 0xa0000 /* FCE */
+#define MIXER_BWS_REGS 0xa2000 /* BWS */
+#define MIXER_LTI_REGS 0xa4000 /* LTI */
+#define MIXER_PEAK_REGS 0xa6000 /* PEAK */
+#define MIXER_ASE_REGS 0xa8000 /* ASE */
+#define MIXER_FCC_REGS 0xaa000 /* FCC */
+#define MIXER_DCSC_REGS 0xb0000 /* DCSC/SMBL */
+
+/* global control */
+#define MIXER_GLB_CTL_REG 0x00
+#define MIXER_GLB_CTL_rt_en BIT(0)
+#define MIXER_GLB_CTL_finish_irq_en BIT(4)
+#define MIXER_GLB_CTL_rtwb_port BIT(12)
+#define MIXER_GLB_STATUS_REG 0x04
+#define MIXER_GLB_DBUFF_REG 0x08
+#define MIXER_GLB_SIZE_REG 0x0c
+
+/* alpha blending */
+#define MIXER_BLD_FCOLOR_CTL_REG 0x00
+#define MIXER_BLD_FCOLOR_CTL_PEN(pipe) (0x0100 << (pipe))
+#define MIXER_BLD_ATTR_N 4 /* number of attribute blocks */
+#define MIXER_BLD_ATTR_SIZE (4 * 4) /* size of an attribute block */
+#define MIXER_BLD_ATTRx_FCOLOR(x) (0x04 + MIXER_BLD_ATTR_SIZE * (x))
+#define MIXER_BLD_ATTRx_INSIZE(x) (0x08 + MIXER_BLD_ATTR_SIZE * (x))
+#define MIXER_BLD_ATTRx_OFFSET(x) (0x0c + MIXER_BLD_ATTR_SIZE * (x))
+#define MIXER_BLD_ROUTE_REG 0x80
+#define MIXER_BLD_ROUTE(chan, pipe) ((chan) << ((pipe) * 4))
+#define MIXER_BLD_PREMULTIPLY_REG 0x84
+#define MIXER_BLD_BKCOLOR_REG 0x88
+#define MIXER_BLD_OUTPUT_SIZE_REG 0x8c
+#define MIXER_BLD_MODEx_REG(x) (0x90 + 4 * (x)) /* x = 0..3 */
+#define MIXER_BLD_MODE_SRCOVER 0x03010301
+#define MIXER_BLD_OUT_CTL_REG 0xfc
+
+/* VI channel (channel 0) */
+#define VI_CFG_N 4 /* number of layers */
+#define VI_CFG_SIZE 0x30 /* size of a layer */
+#define VI_CFGx_ATTR(l) (0x00 + VI_CFG_SIZE * (l))
+#define VI_CFG_ATTR_en BIT(0)
+#define VI_CFG_ATTR_fcolor_en BIT(4)
+#define VI_CFG_ATTR_fmt_SHIFT 8
+#define VI_CFG_ATTR_fmt_MASK GENMASK(12, 8)
+#define VI_CFG_ATTR_ui_sel BIT(15)
+#define VI_CFG_ATTR_top_down BIT(23)
+#define VI_CFGx_SIZE(l) (0x04 + VI_CFG_SIZE * (l))
+#define VI_CFGx_COORD(l) (0x08 + VI_CFG_SIZE * (l))
+#define VI_N_PLANES 3
+#define VI_CFGx_PITCHy(l, p) (0x0c + VI_CFG_SIZE * (l) + 4 * (p))
+#define VI_CFGx_TOP_LADDRy(l, p) (0x18 + VI_CFG_SIZE * (l) + 4 * (p))
+#define VI_CFGx_BOT_LADDRy(l, p) (0x24 + VI_CFG_SIZE * (l) + 4 * (p))
+#define VI_FCOLORx(l) (0xc0 + 4 * (l))
+#define VI_TOP_HADDRx(p) (0xd0 + 4 * (p))
+#define VI_BOT_HADDRx(p) (0xdc + 4 * (p))
+#define VI_OVL_SIZEx(n) (0xe8 + 4 * (n))
+#define VI_HORI_DSx(n) (0xf0 + 4 * (n))
+#define VI_VERT_DSx(n) (0xf8 + 4 * (n))
+#define VI_SIZE 0x100
+
+/* UI channel (channels 1..3) */
+#define UI_CFG_N 4 /* number of layers */
+#define UI_CFG_SIZE (8 * 4) /* size of a layer */
+#define UI_CFGx_ATTR(l) (0x00 + UI_CFG_SIZE * (l))
+#define UI_CFG_ATTR_en BIT(0)
+#define UI_CFG_ATTR_alpmod_SHIFT 1
+#define UI_CFG_ATTR_alpmod_MASK GENMASK(2, 1)
+#define UI_CFG_ATTR_fcolor_en BIT(4)
+#define UI_CFG_ATTR_fmt_SHIFT 8
+#define UI_CFG_ATTR_fmt_MASK GENMASK(12, 8)
+#define UI_CFG_ATTR_top_down BIT(23)
+#define UI_CFG_ATTR_alpha_SHIFT 24
+#define UI_CFG_ATTR_alpha_MASK GENMASK(31, 24)
+#define UI_CFGx_SIZE(l) (0x04 + UI_CFG_SIZE * (l))
+#define UI_CFGx_COORD(l) (0x08 + UI_CFG_SIZE * (l))
+#define UI_CFGx_PITCH(l) (0x0c + UI_CFG_SIZE * (l))
+#define UI_CFGx_TOP_LADDR(l) (0x10 + UI_CFG_SIZE * (l))
+#define UI_CFGx_BOT_LADDR(l) (0x14 + UI_CFG_SIZE * (l))
+#define UI_CFGx_FCOLOR(l) (0x18 + UI_CFG_SIZE * (l))
+#define UI_TOP_HADDR 0x80
+#define UI_BOT_HADDR 0x84
+#define UI_OVL_SIZE 0x88
+#define UI_SIZE 0x8c
+
+/* coordinates and sizes */
+#define XY(x, y) (((y) << 16) | (x))
+#define WH(w, h) ((((h) - 1) << 16) | ((w) - 1))
+
+/* UI video formats */
+#define DE2_FORMAT_ARGB_8888 0
+#define DE2_FORMAT_BGRA_8888 3
+#define DE2_FORMAT_XRGB_8888 4
+#define DE2_FORMAT_RGB_888 8
+#define DE2_FORMAT_BGR_888 9
+
+/* VI video formats */
+#define DE2_FORMAT_YUV422_I_YVYU 1 /* YVYU */
+#define DE2_FORMAT_YUV422_I_UYVY 2 /* UYVY */
+#define DE2_FORMAT_YUV422_I_YUYV 3 /* YUYV */
+#define DE2_FORMAT_YUV422_P 6 /* YYYY UU VV planar */
+#define DE2_FORMAT_YUV420_P 10 /* YYYY U V planar */
+
+/* plane formats */
+static const uint32_t ui_formats[] = {
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_BGRA8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+};
+
+static const uint32_t vi_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_YUYV,
+ DRM_FORMAT_YVYU,
+ DRM_FORMAT_YUV422,
+ DRM_FORMAT_YUV420,
+ DRM_FORMAT_UYVY,
+ DRM_FORMAT_BGRA8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+};
+
+/*
+ * plane table
+ *
+ * The chosen channel/layer assignment of the planes respects
+ * the following constraints:
+ * - the cursor must be in a channel higher than the primary channel
+ * - there are 4 channels in the LCD 0 and only 2 channels in the LCD 1
+ */
+static const struct {
+ u8 chan;
+ u8 layer;
+ u8 pipe;
+ u8 type; /* plane type */
+ const uint32_t *formats;
+ u8 n_formats;
+} plane_tb[] = {
+ [DE2_PRIMARY_PLANE] = { /* primary plane: channel 0 (VI) */
+ 0, 0, 0,
+ DRM_PLANE_TYPE_PRIMARY,
+ ui_formats, ARRAY_SIZE(ui_formats),
+ },
+ [DE2_CURSOR_PLANE] = { /* cursor: channel 1 (UI) */
+ 1, 0, 1,
+ DRM_PLANE_TYPE_CURSOR,
+ ui_formats, ARRAY_SIZE(ui_formats),
+ },
+ {
+ 0, 1, 0, /* 1st overlay: channel 0, layer 1 */
+ DRM_PLANE_TYPE_OVERLAY,
+ vi_formats, ARRAY_SIZE(vi_formats),
+ },
+ {
+ 0, 2, 0, /* 2nd overlay: channel 0, layer 2 */
+ DRM_PLANE_TYPE_OVERLAY,
+ vi_formats, ARRAY_SIZE(vi_formats),
+ },
+ {
+ 0, 3, 0, /* 3rd overlay: channel 0, layer 3 */
+ DRM_PLANE_TYPE_OVERLAY,
+ vi_formats, ARRAY_SIZE(vi_formats),
+ },
+};
+
+static inline void andl_relaxed(void __iomem *addr, u32 val)
+{
+ writel_relaxed(readl_relaxed(addr) & val, addr);
+}
+
+static inline void orl_relaxed(void __iomem *addr, u32 val)
+{
+ writel_relaxed(readl_relaxed(addr) | val, addr);
+}
+
+/* alert the DE processor about changes in a mixer configuration */
+static void de2_mixer_select(struct priv *priv,
+ int mixer,
+ void __iomem *mixer_io)
+{
+ /* select the mixer */
+ andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
+
+ /* double register switch */
+ writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
+}
+
+/*
+ * cleanup a mixer
+ *
+ * This is needed only once after power on.
+ */
+static void de2_mixer_cleanup(struct priv *priv, int mixer,
+ u32 size)
+{
+ void __iomem *mixer_io = priv->mmio;
+ void __iomem *chan_io;
+ u32 data;
+ unsigned int i;
+
+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+ chan_io = mixer_io + MIXER_CHAN_REGS;
+
+ andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
+ writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
+
+ writel_relaxed(MIXER_GLB_CTL_rt_en,
+ mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
+
+ writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
+
+ /*
+ * clear the VI/UI channels
+ * LCD0: 1 VI and 3 UIs
+ * LCD1: 1 VI and 1 UI
+ */
+ memset_io(chan_io, 0, VI_SIZE);
+ memset_io(chan_io + MIXER_CHAN_SZ, 0, UI_SIZE);
+ if (mixer == 0) {
+ memset_io(chan_io + MIXER_CHAN_SZ * 2, 0, UI_SIZE);
+ memset_io(chan_io + MIXER_CHAN_SZ * 3, 0, UI_SIZE);
+ }
+
+ /* alpha blending */
+ writel_relaxed(0x00000001 | /* fcolor for primary */
+ MIXER_BLD_FCOLOR_CTL_PEN(0),
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
+ for (i = 0; i < MIXER_BLD_ATTR_N; i++) {
+ writel_relaxed(0xff000000,
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_FCOLOR(i));
+ writel_relaxed(size,
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_INSIZE(i));
+ writel_relaxed(0,
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_OFFSET(i));
+ }
+ writel_relaxed(0, mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
+
+ /* prepare the pipe route for the planes */
+ data = 0;
+ for (i = 0; i < DE2_N_PLANES; i++)
+ data |= MIXER_BLD_ROUTE(plane_tb[i].chan, plane_tb[i].pipe);
+ writel_relaxed(data, mixer_io + MIXER_BLD_REGS + MIXER_BLD_ROUTE_REG);
+
+ writel_relaxed(0, mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_PREMULTIPLY_REG);
+ writel_relaxed(0xff000000, mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_BKCOLOR_REG);
+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_OUTPUT_SIZE_REG);
+ writel_relaxed(MIXER_BLD_MODE_SRCOVER,
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(0));
+ writel_relaxed(MIXER_BLD_MODE_SRCOVER,
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(1));
+
+ /* disable the enhancements */
+ writel_relaxed(0, mixer_io + MIXER_VSU_REGS);
+ writel_relaxed(0, mixer_io + MIXER_GSU1_REGS);
+ writel_relaxed(0, mixer_io + MIXER_GSU2_REGS);
+ writel_relaxed(0, mixer_io + MIXER_GSU3_REGS);
+ writel_relaxed(0, mixer_io + MIXER_FCE_REGS);
+ writel_relaxed(0, mixer_io + MIXER_BWS_REGS);
+ writel_relaxed(0, mixer_io + MIXER_LTI_REGS);
+ writel_relaxed(0, mixer_io + MIXER_PEAK_REGS);
+ writel_relaxed(0, mixer_io + MIXER_ASE_REGS);
+ writel_relaxed(0, mixer_io + MIXER_FCC_REGS);
+ writel_relaxed(0, mixer_io + MIXER_DCSC_REGS);
+}
+
+/* enable a mixer */
+static void de2_mixer_enable(struct lcd *lcd)
+{
+ struct priv *priv = lcd->priv;
+ void __iomem *mixer_io = priv->mmio;
+ struct drm_display_mode *mode = &lcd->crtc.mode;
+ u32 size = WH(mode->hdisplay, mode->vdisplay);
+ u32 data;
+ int mixer = lcd->mixer;
+ int i;
+
+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+
+ /* if not done yet, start the DE processor */
+ if (!priv->started) {
+ reset_control_deassert(priv->reset);
+ clk_prepare_enable(priv->gate);
+ clk_prepare_enable(priv->clk);
+ }
+ priv->started |= 1 << mixer;
+
+ /* set the A83T clock divider (500 / 2) = 250MHz */
+ if (priv->soc_type == SOC_A83T)
+ writel_relaxed(0x00000011, /* div = 2 for both LCDs */
+ priv->mmio + DE2_DIV_REG);
+
+ /* deassert the mixer and enable its clock */
+ orl_relaxed(priv->mmio + DE2_RESET_REG, mixer == 0 ? 1 : 4);
+ data = 1 << mixer; /* 1 bit / lcd */
+ orl_relaxed(priv->mmio + DE2_GATE_REG, data);
+ orl_relaxed(priv->mmio + DE2_MOD_REG, data);
+
+ /* if not done yet, cleanup and enable */
+ if (!(priv->clean & (1 << mixer))) {
+ priv->clean |= 1 << mixer;
+ de2_mixer_cleanup(priv, mixer, size);
+ return;
+ }
+
+ /* enable */
+ de2_mixer_select(priv, mixer, mixer_io);
+
+ writel_relaxed(MIXER_GLB_CTL_rt_en,
+ mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
+
+ /* set the size of the frame buffer */
+ writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
+ for (i = 0; i < MIXER_BLD_ATTR_N; i++)
+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_ATTRx_INSIZE(i));
+ writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_OUTPUT_SIZE_REG);
+
+ writel_relaxed(mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0,
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
+}
+
+/* enable a LCD (DE mixer) */
+void de2_de_enable(struct lcd *lcd)
+{
+ mutex_lock(&lcd->priv->mutex);
+
+ de2_mixer_enable(lcd);
+
+ mutex_unlock(&lcd->priv->mutex);
+}
+
+/* disable a LCD (DE mixer) */
+void de2_de_disable(struct lcd *lcd)
+{
+ struct priv *priv = lcd->priv;
+ void __iomem *mixer_io = priv->mmio;
+ int mixer = lcd->mixer;
+ u32 data;
+
+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+
+ mutex_lock(&priv->mutex);
+
+ de2_mixer_select(priv, mixer, mixer_io);
+
+ writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
+
+ data = ~(1 << mixer);
+ andl_relaxed(priv->mmio + DE2_MOD_REG, data);
+ andl_relaxed(priv->mmio + DE2_GATE_REG, data);
+ andl_relaxed(priv->mmio + DE2_RESET_REG, data);
+
+ mutex_unlock(&priv->mutex);
+
+ /* if all mixers are disabled, stop the DE */
+ priv->started &= ~(1 << mixer);
+ if (!priv->started) {
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->gate);
+ reset_control_assert(priv->reset);
+ }
+}
+
+static void de2_vi_update(void __iomem *chan_io,
+ struct drm_gem_cma_object *gem,
+ int layer,
+ unsigned int fmt,
+ u32 ui_sel,
+ u32 size,
+ u32 coord,
+ struct drm_framebuffer *fb,
+ u32 screen_size)
+{
+ int i;
+
+ writel_relaxed(VI_CFG_ATTR_en |
+ (fmt << VI_CFG_ATTR_fmt_SHIFT) |
+ ui_sel,
+ chan_io + VI_CFGx_ATTR(layer));
+ writel_relaxed(size, chan_io + VI_CFGx_SIZE(layer));
+ writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
+ for (i = 0; i < VI_N_PLANES; i++) {
+ writel_relaxed(fb->pitches[i] ? fb->pitches[i] :
+ fb->pitches[0],
+ chan_io + VI_CFGx_PITCHy(layer, i));
+ writel_relaxed(gem->paddr + fb->offsets[i],
+ chan_io + VI_CFGx_TOP_LADDRy(layer, i));
+ }
+ writel_relaxed(0xff000000, chan_io + VI_FCOLORx(layer));
+ if (layer == 0) {
+ writel_relaxed(screen_size,
+ chan_io + VI_OVL_SIZEx(0));
+ }
+}
+
+static void de2_ui_update(void __iomem *chan_io,
+ struct drm_gem_cma_object *gem,
+ int layer,
+ unsigned int fmt,
+ u32 alpha_glob,
+ u32 size,
+ u32 coord,
+ struct drm_framebuffer *fb,
+ u32 screen_size)
+{
+ writel_relaxed(UI_CFG_ATTR_en |
+ (fmt << UI_CFG_ATTR_fmt_SHIFT) |
+ alpha_glob,
+ chan_io + UI_CFGx_ATTR(layer));
+ writel_relaxed(size, chan_io + UI_CFGx_SIZE(layer));
+ writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
+ writel_relaxed(fb->pitches[0], chan_io + UI_CFGx_PITCH(layer));
+ writel_relaxed(gem->paddr + fb->offsets[0],
+ chan_io + UI_CFGx_TOP_LADDR(layer));
+ if (layer == 0)
+ writel_relaxed(screen_size, chan_io + UI_OVL_SIZE);
+}
+
+static void de2_plane_update(struct priv *priv, struct lcd *lcd,
+ int plane_num,
+ struct drm_plane_state *state,
+ struct drm_plane_state *old_state)
+{
+ void __iomem *mixer_io = priv->mmio;
+ void __iomem *chan_io;
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_gem_cma_object *gem;
+ u32 size = WH(state->crtc_w, state->crtc_h);
+ u32 coord, screen_size;
+ u32 fcolor;
+ u32 ui_sel, alpha_glob;
+ int mixer = lcd->mixer;
+ int chan, layer, x, y;
+ unsigned int fmt;
+
+ chan = plane_tb[plane_num].chan;
+ layer = plane_tb[plane_num].layer;
+
+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+ chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;
+
+ x = state->crtc_x >= 0 ? state->crtc_x : 0;
+ y = state->crtc_y >= 0 ? state->crtc_y : 0;
+ coord = XY(x, y);
+
+ /* if plane update was delayed, force a full update */
+ if (priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &
+ (1 << plane_num)) {
+ priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &=
+ ~(1 << plane_num);
+
+ /* handle plane move */
+ } else if (fb == old_state->fb) {
+ de2_mixer_select(priv, mixer, mixer_io);
+ if (chan == 0)
+ writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
+ else
+ writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
+ return;
+ }
+
+ gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+ ui_sel = alpha_glob = 0;
+
+ switch (fb->pixel_format) {
+ case DRM_FORMAT_ARGB8888:
+ fmt = DE2_FORMAT_ARGB_8888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ break;
+ case DRM_FORMAT_BGRA8888:
+ fmt = DE2_FORMAT_BGRA_8888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ break;
+ case DRM_FORMAT_XRGB8888:
+ fmt = DE2_FORMAT_XRGB_8888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ alpha_glob = (1 << UI_CFG_ATTR_alpmod_SHIFT) |
+ (0xff << UI_CFG_ATTR_alpha_SHIFT);
+ break;
+ case DRM_FORMAT_RGB888:
+ fmt = DE2_FORMAT_RGB_888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ break;
+ case DRM_FORMAT_BGR888:
+ fmt = DE2_FORMAT_BGR_888;
+ ui_sel = VI_CFG_ATTR_ui_sel;
+ break;
+ case DRM_FORMAT_YUYV:
+ fmt = DE2_FORMAT_YUV422_I_YUYV;
+ break;
+ case DRM_FORMAT_YVYU:
+ fmt = DE2_FORMAT_YUV422_I_YVYU;
+ break;
+ case DRM_FORMAT_YUV422:
+ fmt = DE2_FORMAT_YUV422_P;
+ break;
+ case DRM_FORMAT_YUV420:
+ fmt = DE2_FORMAT_YUV420_P;
+ break;
+ case DRM_FORMAT_UYVY:
+ fmt = DE2_FORMAT_YUV422_I_UYVY;
+ break;
+ default:
+ pr_err("de2_plane_update: format %.4s not yet treated\n",
+ (char *) &fb->pixel_format);
+ return;
+ }
+
+ /* the overlay size is the one of the primary plane */
+ screen_size = plane_num == DE2_PRIMARY_PLANE ?
+ size :
+ readl_relaxed(mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
+
+ /* prepare pipe enable */
+ fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_FCOLOR_CTL_REG);
+ fcolor |= MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe);
+
+ de2_mixer_select(priv, mixer, mixer_io);
+
+ if (chan == 0) /* VI channel */
+ de2_vi_update(chan_io, gem, layer, fmt, ui_sel, size, coord,
+ fb, screen_size);
+ else /* UI channel */
+ de2_ui_update(chan_io, gem, layer, fmt, alpha_glob, size, coord,
+ fb, screen_size);
+ writel_relaxed(fcolor, mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_FCOLOR_CTL_REG);
+}
+
+static int vi_nb_layers(void __iomem *chan_io)
+{
+ int layer, n = 0;
+
+ for (layer = 0; layer < 4; layer++) {
+ if (readl_relaxed(chan_io + VI_CFGx_ATTR(layer)) != 0)
+ n++;
+ }
+
+ return n;
+}
+
+static int ui_nb_layers(void __iomem *chan_io)
+{
+ int layer, n = 0;
+
+ for (layer = 0; layer < 4; layer++) {
+ if (readl_relaxed(chan_io + UI_CFGx_ATTR(layer)) != 0)
+ n++;
+ }
+
+ return n;
+}
+
+static void de2_plane_disable(struct priv *priv,
+ int mixer, int plane_num)
+{
+ void __iomem *mixer_io = priv->mmio;
+ void __iomem *chan_io;
+ u32 fcolor;
+ int chan, layer, n;
+
+ chan = plane_tb[plane_num].chan;
+ layer = plane_tb[plane_num].layer;
+
+ mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
+ chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;
+
+ if (chan == 0)
+ n = vi_nb_layers(chan_io);
+ else
+ n = ui_nb_layers(chan_io);
+
+ fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
+ MIXER_BLD_FCOLOR_CTL_REG);
+
+ de2_mixer_select(priv, mixer, mixer_io);
+
+ if (chan == 0)
+ writel_relaxed(0, chan_io + VI_CFGx_ATTR(layer));
+ else
+ writel_relaxed(0, chan_io + UI_CFGx_ATTR(layer));
+
+ /* disable the pipe if no more active layer */
+ if (n <= 1)
+ writel_relaxed(fcolor &
+ ~MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe),
+ mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
+}
+
+static void de2_drm_plane_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_plane_state *state = plane->state;
+ struct drm_crtc *crtc = state->crtc;
+ struct lcd *lcd = crtc_to_lcd(crtc);
+ struct priv *priv = lcd->priv;
+ int plane_num = plane - lcd->planes;
+
+ /* if the crtc is disabled, mark update delayed */
+ if (!(priv->started & (1 << lcd->mixer))) {
+ lcd->delayed |= 1 << plane_num;
+ return; /* mixer disabled */
+ }
+
+ mutex_lock(&priv->mutex);
+
+ de2_plane_update(priv, lcd, plane_num, state, old_state);
+
+ mutex_unlock(&priv->mutex);
+}
+
+static void de2_drm_plane_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_crtc *crtc = old_state->crtc;
+ struct lcd *lcd = crtc_to_lcd(crtc);
+ struct priv *priv = lcd->priv;
+ int plane_num = plane - lcd->planes;
+
+ if (!(priv->started & (1 << lcd->mixer)))
+ return; /* mixer disabled */
+
+ mutex_lock(&priv->mutex);
+
+ de2_plane_disable(lcd->priv, lcd->mixer, plane_num);
+
+ mutex_unlock(&priv->mutex);
+}
+
+static const struct drm_plane_helper_funcs plane_helper_funcs = {
+ .atomic_update = de2_drm_plane_update,
+ .atomic_disable = de2_drm_plane_disable,
+};
+
+static const struct drm_plane_funcs plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = drm_plane_cleanup,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int de2_one_plane_init(struct drm_device *drm,
+ struct drm_plane *plane,
+ int possible_crtcs,
+ int plane_num)
+{
+ int ret;
+
+ ret = drm_universal_plane_init(drm, plane, possible_crtcs,
+ &plane_funcs,
+ plane_tb[plane_num].formats,
+ plane_tb[plane_num].n_formats,
+ plane_tb[plane_num].type, NULL);
+ if (ret >= 0)
+ drm_plane_helper_add(plane, &plane_helper_funcs);
+
+ return ret;
+}
+
+/* initialize the planes */
+int de2_plane_init(struct drm_device *drm, struct lcd *lcd)
+{
+ int i, n, ret, possible_crtcs = 1 << drm_crtc_index(&lcd->crtc);
+
+ n = ARRAY_SIZE(plane_tb);
+ if (n != DE2_N_PLANES) {
+ dev_err(lcd->dev, "Bug: incorrect number of planes %d != "
+ __stringify(DE2_N_PLANES) "\n", n);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < n; i++) {
+ ret = de2_one_plane_init(drm, &lcd->planes[i],
+ possible_crtcs, i);
+ if (ret < 0) {
+ dev_err(lcd->dev, "plane init failed %d\n", ret);
+ break;
+ }
+ }
+
+ return ret;
+}
--
2.10.2
^ permalink raw reply related
* [PATCH v9 05/11] arm/arm64: vgic: Introduce VENG0 and VENG1 fields to vmcr struct
From: Christoffer Dall @ 2016-11-28 14:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479906118-15832-6-git-send-email-vijay.kilari@gmail.com>
On Wed, Nov 23, 2016 at 06:31:52PM +0530, vijay.kilari at gmail.com wrote:
> From: Vijaya Kumar K <Vijaya.Kumar@cavium.com>
>
> ICC_VMCR_EL2 supports virtual access to ICC_IGRPEN1_EL1.Enable
> and ICC_IGRPEN0_EL1.Enable fields. Add grpen0 and grpen1 member
> variables to struct vmcr to support read and write of these fields.
>
> Also refactor vgic_set_vmcr and vgic_get_vmcr() code.
> Drop ICH_VMCR_CTLR_SHIFT and ICH_VMCR_CTLR_MASK macros and instead
> use ICH_VMCR_EOI* and ICH_VMCR_CBPR* macros
> .
> Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@cavium.com>
> ---
> include/linux/irqchip/arm-gic-v3.h | 2 --
> virt/kvm/arm/vgic/vgic-mmio-v2.c | 16 ----------------
> virt/kvm/arm/vgic/vgic-mmio.c | 16 ++++++++++++++++
> virt/kvm/arm/vgic/vgic-v3.c | 22 ++++++++++++++++++++--
> virt/kvm/arm/vgic/vgic.h | 5 +++++
> 5 files changed, 41 insertions(+), 20 deletions(-)
>
> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
> index b4f8287..406fc3e 100644
> --- a/include/linux/irqchip/arm-gic-v3.h
> +++ b/include/linux/irqchip/arm-gic-v3.h
> @@ -404,8 +404,6 @@
> #define ICH_HCR_EN (1 << 0)
> #define ICH_HCR_UIE (1 << 1)
>
> -#define ICH_VMCR_CTLR_SHIFT 0
> -#define ICH_VMCR_CTLR_MASK (0x21f << ICH_VMCR_CTLR_SHIFT)
> #define ICH_VMCR_CBPR_SHIFT 4
> #define ICH_VMCR_CBPR_MASK (1 << ICH_VMCR_CBPR_SHIFT)
> #define ICH_VMCR_EOIM_SHIFT 9
> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v2.c b/virt/kvm/arm/vgic/vgic-mmio-v2.c
> index 2cb04b7..ad353b5 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio-v2.c
> +++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c
> @@ -212,22 +212,6 @@ static void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu,
> }
> }
>
> -static void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
> -{
> - if (kvm_vgic_global_state.type == VGIC_V2)
> - vgic_v2_set_vmcr(vcpu, vmcr);
> - else
> - vgic_v3_set_vmcr(vcpu, vmcr);
> -}
> -
> -static void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
> -{
> - if (kvm_vgic_global_state.type == VGIC_V2)
> - vgic_v2_get_vmcr(vcpu, vmcr);
> - else
> - vgic_v3_get_vmcr(vcpu, vmcr);
> -}
> -
> #define GICC_ARCH_VERSION_V2 0x2
>
> /* These are for userland accesses only, there is no guest-facing emulation. */
> diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
> index 0d1bc98..f81e0e5 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio.c
> +++ b/virt/kvm/arm/vgic/vgic-mmio.c
> @@ -416,6 +416,22 @@ int vgic_validate_mmio_region_addr(struct kvm_device *dev,
> return -ENXIO;
> }
>
> +void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
> +{
> + if (kvm_vgic_global_state.type == VGIC_V2)
> + vgic_v2_set_vmcr(vcpu, vmcr);
> + else
> + vgic_v3_set_vmcr(vcpu, vmcr);
> +}
> +
> +void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
> +{
> + if (kvm_vgic_global_state.type == VGIC_V2)
> + vgic_v2_get_vmcr(vcpu, vmcr);
> + else
> + vgic_v3_get_vmcr(vcpu, vmcr);
> +}
> +
> /*
> * kvm_mmio_read_buf() returns a value in a format where it can be converted
> * to a byte array and be directly observed as the guest wanted it to appear
> diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
> index 9f0dae3..a3ff04b 100644
> --- a/virt/kvm/arm/vgic/vgic-v3.c
> +++ b/virt/kvm/arm/vgic/vgic-v3.c
> @@ -175,10 +175,19 @@ void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
> {
> u32 vmcr;
>
> - vmcr = (vmcrp->ctlr << ICH_VMCR_CTLR_SHIFT) & ICH_VMCR_CTLR_MASK;
> + /*
> + * Ignore the FIQen bit, because GIC emulation always implies
> + * SRE=1 which means the vFIQEn bit is also RES1.
> + */
> + vmcr = (vmcrp->ctlr & ICC_CTLR_EL1_EOImode_MASK) >>
> + ICC_CTLR_EL1_EOImode_SHIFT;
> + vmcr = (vmcr << ICH_VMCR_EOIM_SHIFT) & ICH_VMCR_EOIM_MASK;
Nit: I think this can be written more nicely as:
vmcr = ((vmcrp->ctlr >> ICC_CTLR_EL1_EOImode_SHIFT)
<< ICH_VMCR_EOIM_SHIFT) & ICH_VMCR_EOIM_MASK;
> + vmcr |= (vmcrp->ctlr << ICH_VMCR_CBPR_SHIFT) & ICH_VMCR_CBPR_MASK;
> vmcr |= (vmcrp->abpr << ICH_VMCR_BPR1_SHIFT) & ICH_VMCR_BPR1_MASK;
> vmcr |= (vmcrp->bpr << ICH_VMCR_BPR0_SHIFT) & ICH_VMCR_BPR0_MASK;
> vmcr |= (vmcrp->pmr << ICH_VMCR_PMR_SHIFT) & ICH_VMCR_PMR_MASK;
> + vmcr |= (vmcrp->grpen0 << ICH_VMCR_ENG0_SHIFT) & ICH_VMCR_ENG0_MASK;
> + vmcr |= (vmcrp->grpen1 << ICH_VMCR_ENG1_SHIFT) & ICH_VMCR_ENG1_MASK;
>
> vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr = vmcr;
> }
> @@ -187,10 +196,19 @@ void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
> {
> u32 vmcr = vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr;
>
> - vmcrp->ctlr = (vmcr & ICH_VMCR_CTLR_MASK) >> ICH_VMCR_CTLR_SHIFT;
> + /*
> + * Ignore the FIQen bit, because GIC emulation always implies
> + * SRE=1 which means the vFIQEn bit is also RES1.
> + */
> + vmcrp->ctlr = (vmcr & ICH_VMCR_EOIM_MASK) >> ICH_VMCR_EOIM_SHIFT;
> + vmcrp->ctlr = (vmcrp->ctlr << ICC_CTLR_EL1_EOImode_SHIFT) &
> + ICC_CTLR_EL1_EOImode_MASK;
similarly, this could be written as:
vmcrp->ctlr = ((vmcr >> ICH_VMCR_EOIM_SHIFT) <<
ICC_CTLR_EL1_EOImode_SHIFT) & ICC_CTLR_EL1_EOImode_MASK;
> + vmcrp->ctlr |= (vmcr & ICH_VMCR_CBPR_MASK) >> ICH_VMCR_CBPR_SHIFT;
> vmcrp->abpr = (vmcr & ICH_VMCR_BPR1_MASK) >> ICH_VMCR_BPR1_SHIFT;
> vmcrp->bpr = (vmcr & ICH_VMCR_BPR0_MASK) >> ICH_VMCR_BPR0_SHIFT;
> vmcrp->pmr = (vmcr & ICH_VMCR_PMR_MASK) >> ICH_VMCR_PMR_SHIFT;
> + vmcrp->grpen0 = (vmcr & ICH_VMCR_ENG0_MASK) >> ICH_VMCR_ENG0_SHIFT;
> + vmcrp->grpen1 = (vmcr & ICH_VMCR_ENG1_MASK) >> ICH_VMCR_ENG1_SHIFT;
> }
>
> #define INITIAL_PENDBASER_VALUE \
> diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
> index 91f58b2..9232791 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -78,6 +78,9 @@ struct vgic_vmcr {
> u32 abpr;
> u32 bpr;
> u32 pmr;
> + /* Below member variable are valid only for GICv3 */
> + u32 grpen0;
> + u32 grpen1;
> };
>
> struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
> @@ -138,6 +141,8 @@ int vgic_v3_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
> int vgic_v3_redist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
> int offset, u32 *val);
> int kvm_register_vgic_device(unsigned long type);
> +void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
> +void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
> int vgic_lazy_init(struct kvm *kvm);
> int vgic_init(struct kvm *kvm);
>
> --
> 1.9.1
>
My comments on style above notwithstanding:
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
^ permalink raw reply
* [PATCH 0/5] Meson GXL and GXM USB support
From: Neil Armstrong @ 2016-11-28 14:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161126145635.24488-1-martin.blumenstingl@googlemail.com>
On 11/26/2016 03:56 PM, Martin Blumenstingl wrote:
> USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> The most obvious change is that GXL and GXM now have one dwc3
> controller and one dwc2 controller (instead of two dwc2 controllers).
> With that there are also new USB PHYs.
>
> Due to lack of hardware I was only able to test this on a board with
> GXM, but as far as I understand the hardware my preparations should be
> correct (so it should also work on GXL).
>
> dwc2 will probably stay unused on most GXM devices since it's limited
> to device mode via some dwc2 hardware configuration register.
>
> dwc3 is probably used on all devices, even if there is more than just
> one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> ports enabled, while on GXM there are three ports enabled (see below
> for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> configuration, meaning that the SoC is limited to high-speed mode.
> On my GXM device the dwc3 hardware configuration forces it into "host
> only" mode.
>
> The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> (on GXM there are only three enabled, but the registers should support
> up to four).
> The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> configuration enforces "host only" mode I was not able to test this. It
> simply takes care of an interrupt and then notifies all related PHYs
> about the new mode.
> The USB2 PHY block is a bit different: I created one PHY driver which
> spans all "PHY ports" because the handling is a bit tricky. It turns
> out that for each available USB port in dwc3's hub the corresponding
> PHY must be enabled (even if there is no physical port - in my case
> port 3 is not connected to anything, but disabling the PHY breaks
> ports 1 and 2 as well).
> I decided not not pass the USB2 PHYs directly to dwc3 due to three
> reasons: 1. the USB3 PHY (which holds a reference to all relevant
> USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> are used with the same controller and thus it makes sense to keep the
> mode consistent across all ports) 2. the dwc3 driver does not support
> passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> passed to it) 3. it is similar to how the vendor reference driver
> manages the PHYs. Please note that this coupling is not a fixed, this
> is all configurable via devicetree (so if the third USB2 PHY has to
> be passed two the dwc2 controller then this is still possible by
> just moving on PHY reference in the .dts).
>
> The coupling of the USB2 and USB3 PHYs is the reason why I sent the
> two drivers in one patch, even though they are handling different IP
> blocks (different registers, etc.).
>
> Unfortunately there are no datasheets available for any of these PHYs.
> Both drivers were written by reading the reference drivers provided by
> Amlogic and analyzing the registers on the kernel that was shipped with
> my board.
>
> As a last note: the dwc3 driver currently only explicitly enables the
> first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
> seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
> DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
> changes to dwc3 are desired any how these should look like, but for now
> it's working fine even without changes there.
>
> lsusb output on GXM for the dwc3 hub:
> Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
> ...
> Hub Port Status:
> Port 1: 0000.0100 power
> Port 2: 0000.0100 power
> Port 3: 0000.0100 power
>
> NOTE: The devicetree changes depend on my previous series:
> "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
>
> NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
> (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
> "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
>
> I have a tree with all dependencies applied available at [2] if
> someone wants a quick way to test this (I don't take any responsibility
> if anything explodes though).
>
> [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
> [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
> [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
>
> Martin Blumenstingl (5):
> Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
> PHYs
> phy: meson: add USB2 and USB3 PHY support for Meson GXL
> arm64: dts: meson-gxl: add USB support
> ARM64: dts: meson-gxm: add GXM specific USB configuration
> ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
>
> .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt | 25 ++
> .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt | 27 ++
> .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi | 12 +
> arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 49 +++
> .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts | 17 +
> arch/arm64/boot/dts/amlogic/meson-gxm.dtsi | 10 +
> drivers/phy/Kconfig | 13 +
> drivers/phy/Makefile | 2 +
> drivers/phy/phy-meson-gxl-usb2.c | 374 ++++++++++++++++++++
> drivers/phy/phy-meson-gxl-usb3.c | 377 +++++++++++++++++++++
> 10 files changed, 906 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
> create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
> create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
> create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
>
With the patchset in [1], tested successfully on a Nexbox A1.
On my Amlogic P230, the two USB-A ports works, the USB-OTG port work in Host mode, but does nothing connected as peripheral.
When trying to enable the DWC2 in peripheral mode, it fails by :
[ 11.609586] dwc2 c9100000.usb: dwc2_core_reset() HANG! Soft Reset GRSTCTL=80000001
[ 11.616962] dwc2 c9100000.usb: Specified GNPTXFDEP=1024 > 768
[ 11.622643] dwc2 c9100000.usb: EPs: 7, dedicated fifos, 712 entries in SPRAM
Tested-by: Neil Armstrong <narmstrong@baylibre.com>
^ permalink raw reply
* [PATCHv2 00/11] Implement clocksource for rockchip SoC using rockchip timer
From: Alexander Kochetkov @ 2016-11-28 14:31 UTC (permalink / raw)
To: linux-arm-kernel
Hello,
This patch series contain:
- devicetree bindings clarification for rockchip timers
- dts files fixes for rk3228-evb, rk3229-evb and rk3188
- implementation of clocksource for rockchip SoC
The clock supplying the arm-global-timer on the rk3188 is coming from the
the cpu clock itself and thus changes its rate everytime cpufreq adjusts
the cpu frequency making this timer unsuitable as a stable clocksource.
The rk3188, rk3288 and following socs share a separate timer block already
handled by the rockchip-timer driver. Therefore adapt this driver to also
be able to act as clocksource on rk3188.
In order to test clocksource you can run following commands and check
how much time it take in real. On rk3188 it take about ~45 seconds.
Such error cannot be fixed using NTP. Haven't test clocksource
on rk3288 and onwards. Guess they can also have unstable clocksource.
cpufreq-set -f 1.6GHZ
date; sleep 60; date
Regards,
Alexander.
This is try 2. Please discard all previous patches:
devicetree:
https://patchwork.ozlabs.org/patch/699019/
https://patchwork.ozlabs.org/patch/699020/
kernel:
https://patchwork.kernel.org/patch/9443975/
https://patchwork.kernel.org/patch/9443971/
https://patchwork.kernel.org/patch/9443959/
https://patchwork.kernel.org/patch/9443963/
https://patchwork.kernel.org/patch/9443979/
https://patchwork.kernel.org/patch/9443989/
https://patchwork.kernel.org/patch/9443987/
https://patchwork.kernel.org/patch/9443977/
https://patchwork.kernel.org/patch/9443991/
Old thread:
http://lists.infradead.org/pipermail/linux-rockchip/2016-November/013147.html
Alexander Kochetkov (11):
dt-bindings: clarify compatible property for rockchip timers
ARM: dts: rockchip: update compatible property for rk3228 timer
ARM: dts: rockchip: update compatible property for rk3229 timer
ARM: dts: rockchip: add timer entries to rk3188 dtsi
clocksource/drivers/rockchip_timer: split bc_timer into rk_timer and
rk_clock_event_device
clocksource/drivers/rockchip_timer: low level routines take rk_timer
as parameter
clocksource/drivers/rockchip_timer: drop unused rk_base() and
rk_ctrl()
clocksource/drivers/rockchip_timer: move TIMER_INT_UNMASK out of
rk_timer_enable()
clocksource/drivers/rockchip_timer: implement loading 64bit value
into timer
clocksource/drivers/rockchip_timer: implement reading 64bit value
from timer
clocksource/drivers/rockchip_timer: implement clocksource timer
.../bindings/timer/rockchip,rk-timer.txt | 12 +-
arch/arm/boot/dts/rk3188.dtsi | 16 ++
arch/arm/boot/dts/rk3228-evb.dts | 4 +
arch/arm/boot/dts/rk3229-evb.dts | 4 +
drivers/clocksource/rockchip_timer.c | 202 +++++++++++++++-----
5 files changed, 184 insertions(+), 54 deletions(-)
--
1.7.9.5
^ permalink raw reply
* [PATCHv2 01/11] dt-bindings: clarify compatible property for rockchip timers
From: Alexander Kochetkov @ 2016-11-28 14:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480343486-25539-1-git-send-email-al.kochet@gmail.com>
Make all properties description in form '"rockchip,<chip>-timer",
"rockchip,rk3288-timer"' for all chips found in linux kernel.
Suggested-by: Heiko St?bner <heiko@sntech.de>
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
---
.../bindings/timer/rockchip,rk-timer.txt | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/timer/rockchip,rk-timer.txt b/Documentation/devicetree/bindings/timer/rockchip,rk-timer.txt
index a41b184..16a5f45 100644
--- a/Documentation/devicetree/bindings/timer/rockchip,rk-timer.txt
+++ b/Documentation/devicetree/bindings/timer/rockchip,rk-timer.txt
@@ -1,9 +1,15 @@
Rockchip rk timer
Required properties:
-- compatible: shall be one of:
- "rockchip,rk3288-timer" - for rk3066, rk3036, rk3188, rk322x, rk3288, rk3368
- "rockchip,rk3399-timer" - for rk3399
+- compatible: should be:
+ "rockchip,rk3036-timer", "rockchip,rk3288-timer": for Rockchip RK3036
+ "rockchip,rk3066-timer", "rockchip,rk3288-timer": for Rockchip RK3066
+ "rockchip,rk3188-timer", "rockchip,rk3288-timer": for Rockchip RK3188
+ "rockchip,rk3228-timer", "rockchip,rk3288-timer": for Rockchip RK3228
+ "rockchip,rk3229-timer", "rockchip,rk3288-timer": for Rockchip RK3229
+ "rockchip,rk3288-timer": for Rockchip RK3288
+ "rockchip,rk3368-timer", "rockchip,rk3288-timer": for Rockchip RK3368
+ "rockchip,rk3399-timer": for Rockchip RK3399
- reg: base address of the timer register starting with TIMERS CONTROL register
- interrupts: should contain the interrupts for Timer0
- clocks : must contain an entry for each entry in clock-names
--
1.7.9.5
^ permalink raw reply related
* [PATCHv2 02/11] ARM: dts: rockchip: update compatible property for rk3228 timer
From: Alexander Kochetkov @ 2016-11-28 14:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480343486-25539-1-git-send-email-al.kochet@gmail.com>
Property set to '"rockchip,rk3228-timer", "rockchip,rk3288-timer"'
to match devicetree bindings.
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
---
arch/arm/boot/dts/rk3228-evb.dts | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm/boot/dts/rk3228-evb.dts b/arch/arm/boot/dts/rk3228-evb.dts
index 904668e..38eab87 100644
--- a/arch/arm/boot/dts/rk3228-evb.dts
+++ b/arch/arm/boot/dts/rk3228-evb.dts
@@ -70,3 +70,7 @@
&uart2 {
status = "okay";
};
+
+&timer {
+ compatible = "rockchip,rk3228-timer", "rockchip,rk3288-timer";
+};
--
1.7.9.5
^ permalink raw reply related
* [PATCHv2 03/11] ARM: dts: rockchip: update compatible property for rk3229 timer
From: Alexander Kochetkov @ 2016-11-28 14:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480343486-25539-1-git-send-email-al.kochet@gmail.com>
Property set to '"rockchip,rk3229-timer", "rockchip,rk3288-timer"'
to match devicetree bindings.
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
---
arch/arm/boot/dts/rk3229-evb.dts | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm/boot/dts/rk3229-evb.dts b/arch/arm/boot/dts/rk3229-evb.dts
index b6a1203..6629769 100644
--- a/arch/arm/boot/dts/rk3229-evb.dts
+++ b/arch/arm/boot/dts/rk3229-evb.dts
@@ -88,3 +88,7 @@
&uart2 {
status = "okay";
};
+
+&timer {
+ compatible = "rockchip,rk3229-timer", "rockchip,rk3288-timer";
+};
--
1.7.9.5
^ permalink raw reply related
* [PATCHv2 04/11] ARM: dts: rockchip: add timer entries to rk3188 dtsi
From: Alexander Kochetkov @ 2016-11-28 14:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480343486-25539-1-git-send-email-al.kochet@gmail.com>
The patch add two timers to all rk3188 based boards.
The first timer is from alive subsystem and it act as a backup
for the local timers at sleep time. It act the same as timers
on other rockchip chips already present in kernel.
The second timer is from CPU subsystem and act as replacement
for the arm-global-timer clocksource. It run as stable frequency
24MHz.
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
---
arch/arm/boot/dts/rk3188.dtsi | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm/boot/dts/rk3188.dtsi b/arch/arm/boot/dts/rk3188.dtsi
index 31f81b2..0dc52fe 100644
--- a/arch/arm/boot/dts/rk3188.dtsi
+++ b/arch/arm/boot/dts/rk3188.dtsi
@@ -106,6 +106,22 @@
};
};
+ timer3: timer at 2000e000 {
+ compatible = "rockchip,rk3188-timer", "rockchip,rk3288-timer";
+ reg = <0x2000e000 0x20>;
+ interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru SCLK_TIMER3>, <&cru PCLK_TIMER3>;
+ clock-names = "timer", "pclk";
+ };
+
+ timer6: timer at 200380a0 {
+ compatible = "rockchip,rk3188-timer", "rockchip,rk3288-timer";
+ reg = <0x200380a0 0x20>;
+ interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru SCLK_TIMER6>, <&cru PCLK_TIMER0>;
+ clock-names = "timer", "pclk";
+ };
+
i2s0: i2s at 1011a000 {
compatible = "rockchip,rk3188-i2s", "rockchip,rk3066-i2s";
reg = <0x1011a000 0x2000>;
--
1.7.9.5
^ permalink raw reply related
* [PATCHv2 05/11] clocksource/drivers/rockchip_timer: split bc_timer into rk_timer and rk_clock_event_device
From: Alexander Kochetkov @ 2016-11-28 14:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480343486-25539-1-git-send-email-al.kochet@gmail.com>
The patch move ce field out of struct bc_timer into struct
rk_clock_event_device and rename struct bc_timer to struct rk_timer.
This is refactoring step without functional changes.
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
---
drivers/clocksource/rockchip_timer.c | 33 ++++++++++++++++++++++-----------
1 file changed, 22 insertions(+), 11 deletions(-)
diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index 23e267a..6d68d4c 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -29,18 +29,28 @@
#define TIMER_MODE_USER_DEFINED_COUNT (1 << 1)
#define TIMER_INT_UNMASK (1 << 2)
-struct bc_timer {
- struct clock_event_device ce;
+struct rk_timer {
void __iomem *base;
void __iomem *ctrl;
u32 freq;
};
-static struct bc_timer bc_timer;
+struct rk_clock_event_device {
+ struct clock_event_device ce;
+ struct rk_timer timer;
+};
+
+static struct rk_clock_event_device bc_timer;
+
+static inline struct rk_clock_event_device*
+rk_clock_event_device(struct clock_event_device *ce)
+{
+ return container_of(ce, struct rk_clock_event_device, ce);
+}
-static inline struct bc_timer *rk_timer(struct clock_event_device *ce)
+static inline struct rk_timer *rk_timer(struct clock_event_device *ce)
{
- return container_of(ce, struct bc_timer, ce);
+ return &rk_clock_event_device(ce)->timer;
}
static inline void __iomem *rk_base(struct clock_event_device *ce)
@@ -116,16 +126,17 @@ static irqreturn_t rk_timer_interrupt(int irq, void *dev_id)
static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg)
{
struct clock_event_device *ce = &bc_timer.ce;
+ struct rk_timer *timer = &bc_timer.timer;
struct clk *timer_clk;
struct clk *pclk;
int ret = -EINVAL, irq;
- bc_timer.base = of_iomap(np, 0);
- if (!bc_timer.base) {
+ timer->base = of_iomap(np, 0);
+ if (!timer->base) {
pr_err("Failed to get base address for '%s'\n", TIMER_NAME);
return -ENXIO;
}
- bc_timer.ctrl = bc_timer.base + ctrl_reg;
+ timer->ctrl = timer->base + ctrl_reg;
pclk = of_clk_get_by_name(np, "pclk");
if (IS_ERR(pclk)) {
@@ -153,7 +164,7 @@ static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg)
goto out_timer_clk;
}
- bc_timer.freq = clk_get_rate(timer_clk);
+ timer->freq = clk_get_rate(timer_clk);
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
@@ -181,7 +192,7 @@ static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg)
goto out_irq;
}
- clockevents_config_and_register(ce, bc_timer.freq, 1, UINT_MAX);
+ clockevents_config_and_register(ce, timer->freq, 1, UINT_MAX);
return 0;
@@ -190,7 +201,7 @@ out_irq:
out_timer_clk:
clk_disable_unprepare(pclk);
out_unmap:
- iounmap(bc_timer.base);
+ iounmap(timer->base);
return ret;
}
--
1.7.9.5
^ permalink raw reply related
* [PATCHv2 06/11] clocksource/drivers/rockchip_timer: low level routines take rk_timer as parameter
From: Alexander Kochetkov @ 2016-11-28 14:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480343486-25539-1-git-send-email-al.kochet@gmail.com>
Pass rk_timer instead of clock_event_device to low lever timer routines.
So that code could be reused by clocksource implementation.
This is refactoring step without functional changes.
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
---
drivers/clocksource/rockchip_timer.c | 47 +++++++++++++++++++---------------
1 file changed, 27 insertions(+), 20 deletions(-)
diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index 6d68d4c..aa9ccd1 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -63,60 +63,67 @@ static inline void __iomem *rk_ctrl(struct clock_event_device *ce)
return rk_timer(ce)->ctrl;
}
-static inline void rk_timer_disable(struct clock_event_device *ce)
+static inline void rk_timer_disable(struct rk_timer *timer)
{
- writel_relaxed(TIMER_DISABLE, rk_ctrl(ce));
+ writel_relaxed(TIMER_DISABLE, timer->ctrl);
}
-static inline void rk_timer_enable(struct clock_event_device *ce, u32 flags)
+static inline void rk_timer_enable(struct rk_timer *timer, u32 flags)
{
writel_relaxed(TIMER_ENABLE | TIMER_INT_UNMASK | flags,
- rk_ctrl(ce));
+ timer->ctrl);
}
static void rk_timer_update_counter(unsigned long cycles,
- struct clock_event_device *ce)
+ struct rk_timer *timer)
{
- writel_relaxed(cycles, rk_base(ce) + TIMER_LOAD_COUNT0);
- writel_relaxed(0, rk_base(ce) + TIMER_LOAD_COUNT1);
+ writel_relaxed(cycles, timer->base + TIMER_LOAD_COUNT0);
+ writel_relaxed(0, timer->base + TIMER_LOAD_COUNT1);
}
-static void rk_timer_interrupt_clear(struct clock_event_device *ce)
+static void rk_timer_interrupt_clear(struct rk_timer *timer)
{
- writel_relaxed(1, rk_base(ce) + TIMER_INT_STATUS);
+ writel_relaxed(1, timer->base + TIMER_INT_STATUS);
}
static inline int rk_timer_set_next_event(unsigned long cycles,
struct clock_event_device *ce)
{
- rk_timer_disable(ce);
- rk_timer_update_counter(cycles, ce);
- rk_timer_enable(ce, TIMER_MODE_USER_DEFINED_COUNT);
+ struct rk_timer *timer = rk_timer(ce);
+
+ rk_timer_disable(timer);
+ rk_timer_update_counter(cycles, timer);
+ rk_timer_enable(timer, TIMER_MODE_USER_DEFINED_COUNT);
return 0;
}
static int rk_timer_shutdown(struct clock_event_device *ce)
{
- rk_timer_disable(ce);
+ struct rk_timer *timer = rk_timer(ce);
+
+ rk_timer_disable(timer);
return 0;
}
static int rk_timer_set_periodic(struct clock_event_device *ce)
{
- rk_timer_disable(ce);
- rk_timer_update_counter(rk_timer(ce)->freq / HZ - 1, ce);
- rk_timer_enable(ce, TIMER_MODE_FREE_RUNNING);
+ struct rk_timer *timer = rk_timer(ce);
+
+ rk_timer_disable(timer);
+ rk_timer_update_counter(timer->freq / HZ - 1, timer);
+ rk_timer_enable(timer, TIMER_MODE_FREE_RUNNING);
return 0;
}
static irqreturn_t rk_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *ce = dev_id;
+ struct rk_timer *timer = rk_timer(ce);
- rk_timer_interrupt_clear(ce);
+ rk_timer_interrupt_clear(timer);
if (clockevent_state_oneshot(ce))
- rk_timer_disable(ce);
+ rk_timer_disable(timer);
ce->event_handler(ce);
@@ -183,8 +190,8 @@ static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg)
ce->cpumask = cpu_possible_mask;
ce->rating = 250;
- rk_timer_interrupt_clear(ce);
- rk_timer_disable(ce);
+ rk_timer_interrupt_clear(timer);
+ rk_timer_disable(timer);
ret = request_irq(irq, rk_timer_interrupt, IRQF_TIMER, TIMER_NAME, ce);
if (ret) {
--
1.7.9.5
^ permalink raw reply related
* [PATCHv2 07/11] clocksource/drivers/rockchip_timer: drop unused rk_base() and rk_ctrl()
From: Alexander Kochetkov @ 2016-11-28 14:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480343486-25539-1-git-send-email-al.kochet@gmail.com>
Use of functions has been ceased by previous commit.
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
---
drivers/clocksource/rockchip_timer.c | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index aa9ccd1..a17dc61 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -53,16 +53,6 @@ static inline struct rk_timer *rk_timer(struct clock_event_device *ce)
return &rk_clock_event_device(ce)->timer;
}
-static inline void __iomem *rk_base(struct clock_event_device *ce)
-{
- return rk_timer(ce)->base;
-}
-
-static inline void __iomem *rk_ctrl(struct clock_event_device *ce)
-{
- return rk_timer(ce)->ctrl;
-}
-
static inline void rk_timer_disable(struct rk_timer *timer)
{
writel_relaxed(TIMER_DISABLE, timer->ctrl);
--
1.7.9.5
^ permalink raw reply related
* [PATCHv2 08/11] clocksource/drivers/rockchip_timer: move TIMER_INT_UNMASK out of rk_timer_enable()
From: Alexander Kochetkov @ 2016-11-28 14:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480343486-25539-1-git-send-email-al.kochet@gmail.com>
This allow to enable timer without enabling interrupts from it.
As that mode will be used in clocksource implementation.
This is refactoring step without functional changes.
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
---
drivers/clocksource/rockchip_timer.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index a17dc61..61c3bb1 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -60,8 +60,7 @@ static inline void rk_timer_disable(struct rk_timer *timer)
static inline void rk_timer_enable(struct rk_timer *timer, u32 flags)
{
- writel_relaxed(TIMER_ENABLE | TIMER_INT_UNMASK | flags,
- timer->ctrl);
+ writel_relaxed(TIMER_ENABLE | flags, timer->ctrl);
}
static void rk_timer_update_counter(unsigned long cycles,
@@ -83,7 +82,8 @@ static inline int rk_timer_set_next_event(unsigned long cycles,
rk_timer_disable(timer);
rk_timer_update_counter(cycles, timer);
- rk_timer_enable(timer, TIMER_MODE_USER_DEFINED_COUNT);
+ rk_timer_enable(timer, TIMER_MODE_USER_DEFINED_COUNT |
+ TIMER_INT_UNMASK);
return 0;
}
@@ -101,7 +101,7 @@ static int rk_timer_set_periodic(struct clock_event_device *ce)
rk_timer_disable(timer);
rk_timer_update_counter(timer->freq / HZ - 1, timer);
- rk_timer_enable(timer, TIMER_MODE_FREE_RUNNING);
+ rk_timer_enable(timer, TIMER_MODE_FREE_RUNNING | TIMER_INT_UNMASK);
return 0;
}
--
1.7.9.5
^ permalink raw reply related
* [PATCHv2 09/11] clocksource/drivers/rockchip_timer: implement loading 64bit value into timer
From: Alexander Kochetkov @ 2016-11-28 14:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480343486-25539-1-git-send-email-al.kochet@gmail.com>
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
---
drivers/clocksource/rockchip_timer.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index 61c3bb1..c2b0454 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -63,11 +63,13 @@ static inline void rk_timer_enable(struct rk_timer *timer, u32 flags)
writel_relaxed(TIMER_ENABLE | flags, timer->ctrl);
}
-static void rk_timer_update_counter(unsigned long cycles,
- struct rk_timer *timer)
+static void rk_timer_update_counter(u64 cycles, struct rk_timer *timer)
{
- writel_relaxed(cycles, timer->base + TIMER_LOAD_COUNT0);
- writel_relaxed(0, timer->base + TIMER_LOAD_COUNT1);
+ u32 lower = cycles & 0xFFFFFFFF;
+ u32 upper = (cycles >> 32) & 0xFFFFFFFF;
+
+ writel_relaxed(lower, timer->base + TIMER_LOAD_COUNT0);
+ writel_relaxed(upper, timer->base + TIMER_LOAD_COUNT1);
}
static void rk_timer_interrupt_clear(struct rk_timer *timer)
--
1.7.9.5
^ permalink raw reply related
* [PATCHv2 10/11] clocksource/drivers/rockchip_timer: implement reading 64bit value from timer
From: Alexander Kochetkov @ 2016-11-28 14:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480343486-25539-1-git-send-email-al.kochet@gmail.com>
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
---
drivers/clocksource/rockchip_timer.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index c2b0454..6224aa9 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -19,6 +19,8 @@
#define TIMER_LOAD_COUNT0 0x00
#define TIMER_LOAD_COUNT1 0x04
+#define TIMER_CURRENT_VALUE0 0x08
+#define TIMER_CURRENT_VALUE1 0x0C
#define TIMER_CONTROL_REG3288 0x10
#define TIMER_CONTROL_REG3399 0x1c
#define TIMER_INT_STATUS 0x18
@@ -72,6 +74,25 @@ static void rk_timer_update_counter(u64 cycles, struct rk_timer *timer)
writel_relaxed(upper, timer->base + TIMER_LOAD_COUNT1);
}
+static u64 rk_timer_counter_read(struct rk_timer *timer)
+{
+ u64 counter;
+ u32 lower;
+ u32 upper, old_upper;
+
+ upper = readl_relaxed(timer->base + TIMER_CURRENT_VALUE1);
+ do {
+ old_upper = upper;
+ lower = readl_relaxed(timer->base + TIMER_CURRENT_VALUE0);
+ upper = readl_relaxed(timer->base + TIMER_CURRENT_VALUE1);
+ } while (upper != old_upper);
+
+ counter = upper;
+ counter <<= 32;
+ counter |= lower;
+ return counter;
+}
+
static void rk_timer_interrupt_clear(struct rk_timer *timer)
{
writel_relaxed(1, timer->base + TIMER_INT_STATUS);
--
1.7.9.5
^ permalink raw reply related
* [PATCHv2 11/11] clocksource/drivers/rockchip_timer: implement clocksource timer
From: Alexander Kochetkov @ 2016-11-28 14:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480343486-25539-1-git-send-email-al.kochet@gmail.com>
The clock supplying the arm-global-timer on the rk3188 is coming from the
the cpu clock itself and thus changes its rate everytime cpufreq adjusts
the cpu frequency making this timer unsuitable as a stable clocksource.
The rk3188, rk3288 and following socs share a separate timer block already
handled by the rockchip-timer driver. Therefore adapt this driver to also
be able to act as clocksource on rk3188.
In order to test clocksource you can run following commands and check
how much time it take in real. On rk3188 it take about ~45 seconds.
Such error cannot be fixed using NTP. Haven't test clocksource
on rk3288 and onwards. Guess they can also have unstable clocksource.
cpufreq-set -f 1.6GHZ
date; sleep 60; date
In order to use the patch you need to declare two timers in the dts
file. The first timer will be initialized as clockevent provider
and the second one as clocksource. The clockevent must be from
alive subsystem as it used as backup for the local timers at sleep
time.
In order to resolve ambiguity between timers in the device tree,
it is possible to correctly number the timers using "aliases" node.
The patch does not break compatibility with older device tree files.
The older device tree files contain only one timer. The timer
will be initialized as clockevent, as expected.
Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
---
drivers/clocksource/rockchip_timer.c | 101 ++++++++++++++++++++++++++++------
1 file changed, 85 insertions(+), 16 deletions(-)
diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index 6224aa9..430b182 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -11,6 +11,7 @@
#include <linux/clockchips.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/sched_clock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -42,7 +43,19 @@ struct rk_clock_event_device {
struct rk_timer timer;
};
+struct rk_clocksource {
+ struct clocksource cs;
+ struct rk_timer timer;
+};
+
+enum {
+ ROCKCHIP_CLKSRC_CLOCKEVENT = 0,
+ ROCKCHIP_CLKSRC_CLOCKSOURCE = 1,
+};
+
static struct rk_clock_event_device bc_timer;
+static struct rk_clocksource cs_timer;
+static int rk_next_clksrc = ROCKCHIP_CLKSRC_CLOCKEVENT;
static inline struct rk_clock_event_device*
rk_clock_event_device(struct clock_event_device *ce)
@@ -143,13 +156,46 @@ static irqreturn_t rk_timer_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static cycle_t rk_timer_clocksource_read(struct clocksource *cs)
+{
+ struct rk_clocksource *_cs =
+ container_of(cs, struct rk_clocksource, cs);
+
+ return ~rk_timer_counter_read(&_cs->timer);
+}
+
+static u64 notrace rk_timer_sched_clock_read(void)
+{
+ struct rk_clocksource *_cs = &cs_timer;
+
+ return ~rk_timer_counter_read(&_cs->timer);
+}
+
static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg)
{
- struct clock_event_device *ce = &bc_timer.ce;
- struct rk_timer *timer = &bc_timer.timer;
+ struct clock_event_device *ce = NULL;
+ struct clocksource *cs = NULL;
+ struct rk_timer *timer = NULL;
struct clk *timer_clk;
struct clk *pclk;
int ret = -EINVAL, irq;
+ int clksrc;
+
+ clksrc = rk_next_clksrc;
+ rk_next_clksrc++;
+
+ switch (clksrc) {
+ case ROCKCHIP_CLKSRC_CLOCKEVENT:
+ ce = &bc_timer.ce;
+ timer = &bc_timer.timer;
+ break;
+ case ROCKCHIP_CLKSRC_CLOCKSOURCE:
+ cs = &cs_timer.cs;
+ timer = &cs_timer.timer;
+ break;
+ default:
+ return -ENODEV;
+ }
timer->base = of_iomap(np, 0);
if (!timer->base) {
@@ -193,26 +239,49 @@ static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg)
goto out_irq;
}
- ce->name = TIMER_NAME;
- ce->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
- CLOCK_EVT_FEAT_DYNIRQ;
- ce->set_next_event = rk_timer_set_next_event;
- ce->set_state_shutdown = rk_timer_shutdown;
- ce->set_state_periodic = rk_timer_set_periodic;
- ce->irq = irq;
- ce->cpumask = cpu_possible_mask;
- ce->rating = 250;
+ if (ce) {
+ ce->name = TIMER_NAME;
+ ce->features = CLOCK_EVT_FEAT_PERIODIC |
+ CLOCK_EVT_FEAT_ONESHOT |
+ CLOCK_EVT_FEAT_DYNIRQ;
+ ce->set_next_event = rk_timer_set_next_event;
+ ce->set_state_shutdown = rk_timer_shutdown;
+ ce->set_state_periodic = rk_timer_set_periodic;
+ ce->irq = irq;
+ ce->cpumask = cpu_possible_mask;
+ ce->rating = 250;
+ }
+
+ if (cs) {
+ cs->name = TIMER_NAME;
+ cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
+ cs->mask = CLOCKSOURCE_MASK(64);
+ cs->read = rk_timer_clocksource_read;
+ cs->rating = 350;
+ }
rk_timer_interrupt_clear(timer);
rk_timer_disable(timer);
- ret = request_irq(irq, rk_timer_interrupt, IRQF_TIMER, TIMER_NAME, ce);
- if (ret) {
- pr_err("Failed to initialize '%s': %d\n", TIMER_NAME, ret);
- goto out_irq;
+ if (ce) {
+ ret = request_irq(irq, rk_timer_interrupt, IRQF_TIMER,
+ TIMER_NAME, ce);
+ if (ret) {
+ pr_err("Failed to initialize '%s': %d\n",
+ TIMER_NAME, ret);
+ goto out_irq;
+ }
+
+ clockevents_config_and_register(ce, timer->freq, 1, UINT_MAX);
}
- clockevents_config_and_register(ce, timer->freq, 1, UINT_MAX);
+ if (cs) {
+ rk_timer_update_counter(U64_MAX, timer);
+ rk_timer_enable(timer, 0);
+ clocksource_register_hz(cs, timer->freq);
+ sched_clock_register(rk_timer_sched_clock_read, 64,
+ timer->freq);
+ }
return 0;
--
1.7.9.5
^ permalink raw reply related
* Adding a .platform_init callback to sdhci_arasan_ops
From: Sebastian Frias @ 2016-11-28 14:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <e32d2a01-ba3c-6127-51ba-a1fd4176e32c@laposte.net>
On 28/11/16 14:28, Sebastian Frias wrote:
> On 28/11/16 12:44, Adrian Hunter wrote:
>> On 28/11/16 13:20, Sebastian Frias wrote:
>>> Hi Adrian,
>>>
>>> On 28/11/16 11:30, Adrian Hunter wrote:
>>>> On 28/11/16 09:32, Michal Simek wrote:
>>>>> +Sai for Xilinx perspective.
>>>>>
>>>>> On 25.11.2016 16:24, Sebastian Frias wrote:
>>>>>> Hi,
>>>>>>
>>>>>> When using the Arasan SDHCI HW IP, there is a set of parameters called
>>>>>> "Hardware initialized registers"
>>>>>>
>>>>>> (Table 7, Section "Pin Signals", page 56 of Arasan "SD3.0/SDIO3.0/eMMC4.4
>>>>>> AHB Host Controller", revision 6.0 document)
>>>>>>
>>>>>> In some platforms those signals are connected to registers that need to
>>>>>> be programmed at some point for proper driver/HW initialisation.
>>>>>>
>>>>>> I found that the 'struct sdhci_ops' contains a '.platform_init' callback
>>>>>> that is called from within 'sdhci_pltfm_init', and that seems a good
>>>>>> candidate for a place to program those registers (*).
>>>>>>
>>>>>> Do you agree?
>>>>
>>>> We already killed .platform_init
>>>
>>> I just saw that, yet it was the perfect place for the HW initialisation I'm
>>> talking about.
>>> Any way we can restore it?
>>
>> It doesn't serve any purpose I am aware of.
>
> It would serve (for me) if it was there :-)
>
>>
>>>
>>>>
>>>> What is wrong with sdhci_arasan_probe()?
>>>
>>> Well, in 4.7 sdhci_arasan_probe() did not call of_match_device(), so I had
>>> put a call to it just before sdhci_pltfm_init(), something like:
>>>
>>> +static const struct of_device_id sdhci_arasan_of_match[] = {
>>> + {
>>> + .compatible = "arasan,sdhci-8.9a",
>>> + .data = &sdhci_arasan_ops,
>>> + },
>>> + {
>>> + .compatible = "arasan,sdhci-5.1",
>>> + .data = &sdhci_arasan_ops,
>>> + },
>>> + {
>>> + .compatible = "arasan,sdhci-4.9a",
>>> + .data = &sdhci_arasan_ops,
>>> + },
>>> + {
>>> + .compatible = "sigma,smp8734-sdio",
>>> + .data = &sdhci_arasan_tango4_ops,
>>> + },
>>> + { }
>>> +};
>>> +MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
>>>
>>> ...
>>>
>>> + const struct of_device_id *match;
>>> +
>>> + match = of_match_device(sdhci_arasan_of_match, &pdev->dev);
>>> + if (match)
>>> + sdhci_arasan_pdata.ops = match->data;
>>>
>>> where 'sdhci_arasan_tango4_ops' contained a pointer to a .platform_init
>>> callback.
>>>
>>> However, as I stated earlier, an upstream commit:
>>>
>>> commit 3ea4666e8d429223fbb39c1dccee7599ef7657d5
>>> Author: Douglas Anderson <dianders@chromium.org>
>>> Date: Mon Jun 20 10:56:47 2016 -0700
>>>
>>> mmc: sdhci-of-arasan: Properly set corecfg_baseclkfreq on rk3399
>>>
>>> changed struct 'sdhci_arasan_of_match' to convey different data, which
>>> means that instead of having a generic way of accessing such data (such
>>> as 'of_match_device()' and ".data" field), one must also check for
>>> specific "compatible" strings to make sense of the ".data" field, such as
>>> "rockchip,rk3399-sdhci-5.1"
>>>
>>> With the current code:
>>> - there's no 'of_match_device()' before 'sdhci_pltfm_init()'
>>> - the sdhci_pltfm_init() call is made with a static 'sdhci_arasan_pdata'
>>> struct (so it cannot be made dependent on the "compatible" string).
>>> - since 'sdhci_arasan_pdata' is the same for all compatible devices, even
>>> for those that require special handling, more "compatible" matching code is
>>> required
>>> - leading to spread "compatible" matching code; IMHO it would be cleaner if
>>> the 'sdhci_arasan_probe()' code was generic, with just a generic "compatible"
>>> matching, which then proceeded with specific initialisation and generic
>>> initialisation.
>>>
>>> In a nutshell, IMHO it would be better if adding support for more SoCs only
>>> involved changing just 'sdhci_arasan_of_match' without the need to change
>>> 'sdhci_arasan_probe()'.
>>> That would clearly separate the generic and "SoC"-specific code, thus allowing
>>> better maintenance.
>>>
>>> Does that makes sense to you guys?
>>
>> If you want to do that, then why not define your match data with your own
>> callbacks. e.g. something like
>>
>> struct sdhci_arasan_of_data {
>> struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
>> void (*platform_init)(struct sdhci_arasan_data *sdhci_arasan);
>> };
>>
>> struct sdhci_arasan_of_data *data;
>>
>> data = match->data;
>> sdhci_arasan->soc_ctl_map = data->soc_ctl_map;
>> if (data->platform_init)
>> platform_init(sdhci_arasan);
>
> Well, that adds a level in the hierarchy, but here is what it would look like:
>
>
> diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
> index 410a55b..1cb3861 100644
> --- a/drivers/mmc/host/sdhci-of-arasan.c
> +++ b/drivers/mmc/host/sdhci-of-arasan.c
> @@ -382,22 +382,6 @@ static int sdhci_arasan_resume(struct device *dev)
> static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
> sdhci_arasan_resume);
>
> -static const struct of_device_id sdhci_arasan_of_match[] = {
> - /* SoC-specific compatible strings w/ soc_ctl_map */
> - {
> - .compatible = "rockchip,rk3399-sdhci-5.1",
> - .data = &rk3399_soc_ctl_map,
> - },
> -
> - /* Generic compatible below here */
> - { .compatible = "arasan,sdhci-8.9a" },
> - { .compatible = "arasan,sdhci-5.1" },
> - { .compatible = "arasan,sdhci-4.9a" },
> -
> - { /* sentinel */ }
> -};
> -MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
> -
> /**
> * sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate
> *
> @@ -578,6 +562,53 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev)
> of_clk_del_provider(dev->of_node);
> }
>
> +static void sdhci_tango4_platform_init(struct sdhci_host *host)
> +{
> + printk("%s\n", __func__);
> +
> + /*
> + pad_mode[2:0]=0 must be 0
> + sel_sdio[3]=1 must be 1 for SDIO
> + inv_sdwp_pol[4]=0 if set inverts the SD write protect polarity
> + inv_sdcd_pol[5]=0 if set inverts the SD card present polarity
> + */
> + sdhci_writel(host, 0x00000008, 0x100 + 0x0);
> +}
> +
> +struct sdhci_arasan_chip_specific_data {
> + const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
> + void (*platform_init)(struct sdhci_host *host);
> +};
> +
> +static const struct sdhci_arasan_chip_specific_data sdhci_arasan_rockchip = {
> + .soc_ctl_map = &rk3399_soc_ctl_map,
> +};
> +
> +static const struct sdhci_arasan_chip_specific_data sdhci_arasan_sigma = {
> + .platform_init = sdhci_tango4_platform_init,
> +};
> +
> +static const struct of_device_id sdhci_arasan_of_match[] = {
> + /* SoC-specific compatible strings w/ soc_ctl_map */
> + {
> + .compatible = "rockchip,rk3399-sdhci-5.1",
> + .data = &sdhci_arasan_rockchip,
> + },
> + {
> + .compatible = "sigma,sdio-v1",
> + .data = &sdhci_arasan_sigma,
> + },
> +
> + /* Generic compatible below here */
> + { .compatible = "arasan,sdhci-8.9a" },
> + { .compatible = "arasan,sdhci-5.1" },
> + { .compatible = "arasan,sdhci-4.9a" },
> +
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
> +
> +
> static int sdhci_arasan_probe(struct platform_device *pdev)
> {
> int ret;
> @@ -587,6 +618,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 sdhci_arasan_chip_specific_data *sdhci_arasan_chip_specific;
> struct device_node *np = pdev->dev.of_node;
>
> host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata,
> @@ -599,7 +631,11 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
> sdhci_arasan->host = host;
>
> match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
> - sdhci_arasan->soc_ctl_map = match->data;
> + sdhci_arasan_chip_specific = (struct sdhci_arasan_chip_specific_data *)match;
> + if (sdhci_arasan_chip_specific->soc_ctl_map)
> + sdhci_arasan->soc_ctl_map = sdhci_arasan_chip_specific->soc_ctl_map;
> + if (sdhci_arasan_chip_specific->platform_init)
> + sdhci_arasan_chip_specific->platform_init(host);
>
> node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
> if (node) {
>
>
> I will try to send another patch with what a different approach
>
Here's a different approach (I just tested that it built, because I don't have the
rk3399 platform):
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index 410a55b..5be6e67 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -382,22 +382,6 @@ static int sdhci_arasan_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
sdhci_arasan_resume);
-static const struct of_device_id sdhci_arasan_of_match[] = {
- /* SoC-specific compatible strings w/ soc_ctl_map */
- {
- .compatible = "rockchip,rk3399-sdhci-5.1",
- .data = &rk3399_soc_ctl_map,
- },
-
- /* Generic compatible below here */
- { .compatible = "arasan,sdhci-8.9a" },
- { .compatible = "arasan,sdhci-5.1" },
- { .compatible = "arasan,sdhci-4.9a" },
-
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
-
/**
* sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate
*
@@ -578,28 +562,18 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev)
of_clk_del_provider(dev->of_node);
}
-static int sdhci_arasan_probe(struct platform_device *pdev)
+static int sdhci_rockchip_platform_init(struct sdhci_host *host,
+ struct platform_device *pdev)
{
int ret;
- const struct of_device_id *match;
struct device_node *node;
- struct clk *clk_xin;
- 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));
- if (IS_ERR(host))
- return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
- sdhci_arasan->host = host;
- match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
- sdhci_arasan->soc_ctl_map = match->data;
+ sdhci_arasan->soc_ctl_map = &rk3399_soc_ctl_map;
node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
if (node) {
@@ -611,10 +585,107 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Can't get syscon: %d\n",
ret);
- goto err_pltfm_free;
+ return -1;
}
}
+ if (of_property_read_bool(pdev->dev.of_node, "xlnx,fails-without-test-cd"))
+ sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_FORCE_CDTEST;
+
+ return 0;
+}
+
+static int sdhci_rockchip_clock_init(struct sdhci_host *host,
+ struct platform_device *pdev)
+{
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_arasan_data *sdhci_arasan;
+
+ pltfm_host = sdhci_priv(host);
+ sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "rockchip,rk3399-sdhci-5.1"))
+ sdhci_arasan_update_clockmultiplier(host, 0x0);
+
+ sdhci_arasan_update_baseclkfreq(host);
+
+ return sdhci_arasan_register_sdclk(sdhci_arasan, pltfm_host->clk, &pdev->dev);
+}
+
+static int sdhci_tango_platform_init(struct sdhci_host *host,
+ struct platform_device *pdev)
+{
+ return 0;
+}
+
+/* Chip-specific ops */
+struct sdhci_arasan_cs_ops {
+ int (*platform_init)(struct sdhci_host *host,
+ struct platform_device *pdev);
+ int (*clock_init)(struct sdhci_host *host,
+ struct platform_device *pdev);
+};
+
+static const struct sdhci_arasan_cs_ops sdhci_rockchip_cs_ops = {
+ .platform_init = sdhci_rockchip_platform_init,
+ .clock_init = sdhci_rockchip_clock_init,
+};
+
+static const struct sdhci_arasan_cs_ops sdhci_tango_cs_ops = {
+ .platform_init = sdhci_tango_platform_init,
+};
+
+static const struct of_device_id sdhci_arasan_of_match[] = {
+ /* SoC-specific compatible strings */
+ {
+ .compatible = "rockchip,rk3399-sdhci-5.1",
+ .data = &sdhci_rockchip_cs_ops,
+ },
+ {
+ .compatible = "sigma,sdio-v1",
+ .data = &sdhci_tango_cs_ops,
+ },
+
+ /* Generic compatible below here */
+ { .compatible = "arasan,sdhci-8.9a" },
+ { .compatible = "arasan,sdhci-5.1" },
+ { .compatible = "arasan,sdhci-4.9a" },
+
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
+
+static int sdhci_arasan_probe(struct platform_device *pdev)
+{
+ int ret;
+ const struct of_device_id *match;
+ struct clk *clk_xin;
+ struct sdhci_host *host;
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_arasan_data *sdhci_arasan;
+ const struct sdhci_arasan_cs_ops *cs_ops;
+
+ host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata,
+ sizeof(*sdhci_arasan));
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ pltfm_host = sdhci_priv(host);
+ sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ sdhci_arasan->host = host;
+
+ match = of_match_device(sdhci_arasan_of_match, &pdev->dev);
+ if (match)
+ cs_ops = match->data;
+
+ /* SoC-specific platform init */
+ if (cs_ops && cs_ops->platform_init) {
+ ret = cs_ops->platform_init(host, pdev);
+ if (ret)
+ goto err_pltfm_free;
+ }
+
sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
if (IS_ERR(sdhci_arasan->clk_ahb)) {
dev_err(&pdev->dev, "clk_ahb clock not found.\n");
@@ -642,21 +713,14 @@ 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,
- "rockchip,rk3399-sdhci-5.1"))
- sdhci_arasan_update_clockmultiplier(host, 0x0);
-
- sdhci_arasan_update_baseclkfreq(host);
-
- ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev);
- if (ret)
- goto clk_disable_all;
+ /* SoC-specific clock init */
+ if (cs_ops && cs_ops->clock_init) {
+ ret = cs_ops->clock_init(host, pdev);
+ if (ret)
+ goto clk_disable_all;
+ }
ret = mmc_of_parse(host->mmc);
if (ret) {
If you are ok with it I can clean it up to submit it properly.
^ permalink raw reply related
* [PATCH 4/4] crypto: arm/crct10dif - port x86 SSE implementation to ARM
From: Ard Biesheuvel @ 2016-11-28 14:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161128131748.GA2757@gondor.apana.org.au>
On 28 November 2016 at 14:17, Herbert Xu <herbert@gondor.apana.org.au> wrote:
> On Thu, Nov 24, 2016 at 05:32:42PM +0000, Ard Biesheuvel wrote:
>> On 24 November 2016 at 15:43, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
>> > This is a straight transliteration of the Intel algorithm implemented
>> > using SSE and PCLMULQDQ instructions that resides under in the file
>> > arch/x86/crypto/crct10dif-pcl-asm_64.S.
>> >
>> > Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>> > ---
>> > arch/arm/crypto/Kconfig | 5 +
>> > arch/arm/crypto/Makefile | 2 +
>> > arch/{arm64 => arm}/crypto/crct10dif-ce-core.S | 457 +++++++++++---------
>> > arch/{arm64 => arm}/crypto/crct10dif-ce-glue.c | 23 +-
>> > 4 files changed, 277 insertions(+), 210 deletions(-)
>> >
>>
>> This patch needs the following hunk folded in to avoid breaking the
>> Thumb2 build:
>>
>> """
>> diff --git a/arch/arm/crypto/crct10dif-ce-core.S
>> b/arch/arm/crypto/crct10dif-ce-core.S
>> index 30168b0f8581..4fdbca94dd0c 100644
>> --- a/arch/arm/crypto/crct10dif-ce-core.S
>> +++ b/arch/arm/crypto/crct10dif-ce-core.S
>> @@ -152,7 +152,8 @@ CPU_LE( vrev64.8 q7, q7 )
>> // XOR the initial_crc value
>> veor.8 q0, q0, q10
>>
>> - adrl ip, rk3
>> +ARM( adrl ip, rk3 )
>> +THUMB( adr ip, rk3 )
>> vld1.64 {q10}, [ip] // xmm10 has rk3 and rk4
>> // type of pmull instruction
>> // will determine which constant to use
>> """
>
> I'm sorry but this patch doesn't apply on top of the other four.
> So please resend the whole series.
>
Yes, please disregard all CRC ARM/arm64 patches for now, I will
consolidate them into a single v2 and send it out after the merge
window.
^ permalink raw reply
* [PATCH] drm/atmel-hlcdc: Rework the fbdev creation logic
From: Boris Brezillon @ 2016-11-28 15:01 UTC (permalink / raw)
To: linux-arm-kernel
Now that we wait for DRM panels to be available before registering the
DRM device (returning -EPROBE_DEFER if the panel has not been probed
yet), we no longer need to put the fbdev creation code in
->output_poll_changed().
This removes the 10 secs delay between DRM dev registration and fbdev
creation (polling period = 10 seconds).
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Reported-by: Alex Vazquez <avazquez.dev@gmail.com>
---
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
index 5f484310bee9..2325de7c5c6f 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
@@ -431,15 +431,8 @@ static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
{
struct atmel_hlcdc_dc *dc = dev->dev_private;
- if (dc->fbdev) {
+ if (dc->fbdev)
drm_fbdev_cma_hotplug_event(dc->fbdev);
- } else {
- dc->fbdev = drm_fbdev_cma_init(dev, 24,
- dev->mode_config.num_crtc,
- dev->mode_config.num_connector);
- if (IS_ERR(dc->fbdev))
- dc->fbdev = NULL;
- }
}
struct atmel_hlcdc_dc_commit {
@@ -652,10 +645,13 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
platform_set_drvdata(pdev, dev);
- drm_kms_helper_poll_init(dev);
+ dc->fbdev = drm_fbdev_cma_init(dev, 24,
+ dev->mode_config.num_crtc,
+ dev->mode_config.num_connector);
+ if (IS_ERR(dc->fbdev))
+ dc->fbdev = NULL;
- /* force connectors detection */
- drm_helper_hpd_irq_event(dev);
+ drm_kms_helper_poll_init(dev);
return 0;
--
2.7.4
^ permalink raw reply related
* [PATCH v28 4/9] arm64: kdump: implement machine_crash_shutdown()
From: Catalin Marinas @ 2016-11-28 15:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161124095809.7092-3-takahiro.akashi@linaro.org>
On Thu, Nov 24, 2016 at 06:58:05PM +0900, AKASHI Takahiro wrote:
> diff --git a/arch/arm64/include/asm/kexec.h b/arch/arm64/include/asm/kexec.h
> index 04744dc..b5168e8 100644
> --- a/arch/arm64/include/asm/kexec.h
> +++ b/arch/arm64/include/asm/kexec.h
> @@ -40,7 +40,47 @@
> static inline void crash_setup_regs(struct pt_regs *newregs,
> struct pt_regs *oldregs)
> {
> - /* Empty routine needed to avoid build errors. */
> + if (oldregs) {
> + memcpy(newregs, oldregs, sizeof(*newregs));
> + } else {
> + u64 tmp1, tmp2;
> +
> + __asm__ __volatile__ (
> + "stp x0, x1, [%2, #16 * 0]\n"
> + "stp x2, x3, [%2, #16 * 1]\n"
> + "stp x4, x5, [%2, #16 * 2]\n"
> + "stp x6, x7, [%2, #16 * 3]\n"
> + "stp x8, x9, [%2, #16 * 4]\n"
> + "stp x10, x11, [%2, #16 * 5]\n"
> + "stp x12, x13, [%2, #16 * 6]\n"
> + "stp x14, x15, [%2, #16 * 7]\n"
> + "stp x16, x17, [%2, #16 * 8]\n"
> + "stp x18, x19, [%2, #16 * 9]\n"
> + "stp x20, x21, [%2, #16 * 10]\n"
> + "stp x22, x23, [%2, #16 * 11]\n"
> + "stp x24, x25, [%2, #16 * 12]\n"
> + "stp x26, x27, [%2, #16 * 13]\n"
> + "stp x28, x29, [%2, #16 * 14]\n"
> + "mov %0, sp\n"
> + "stp x30, %0, [%2, #16 * 15]\n"
> +
> + "/* faked current PSTATE */\n"
> + "mrs %0, CurrentEL\n"
> + "mrs %1, SPSEL\n"
> + "orr %0, %0, %1\n"
> + "mrs %1, DAIF\n"
> + "orr %0, %0, %1\n"
> + "mrs %1, NZCV\n"
> + "orr %0, %0, %1\n"
> + /* pc */
> + "adr %1, 1f\n"
> + "1:\n"
> + "stp %1, %0, [%2, #16 * 16]\n"
> + : "+r" (tmp1), "+r" (tmp2)
> + : "r" (newregs)
> + : "memory"
tmp1 and tmp2 are not input arguments here, so you should use the "=&"
modifier. With my compiler, I get warnings of these variables being used
uninitialised.
--
Catalin
^ permalink raw reply
* [PATCH 7/10] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC
From: Ulf Hansson @ 2016-11-28 15:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <10a885f0-82e9-a35c-f62f-3fc4518ea8b4@marvell.com>
On 28 November 2016 at 12:38, Ziji Hu <huziji@marvell.com> wrote:
> Hi Ulf,
>
> On 2016/11/28 19:13, Ulf Hansson wrote:
>>>
>>> As you suggest, I replace mmc_wait_for_cmd() with mmc_send_tuning(), to
>>> send commands for testing current sampling point set in our host PHY.
>>>
>>> According to my test result, it shows that mmc_send_tuning() can only support
>>> tuning command (CMD21/CMD19).
>>> As a result, we cannot use mmc_send_tuning() when card is in the speed modes
>>> which doesn't support tuning, such as eMMC HS SDR, eMMC HS DRR and
>>> SD SDR 12/SDR25/DDR50. Card will not response to tuning commands in those
>>> speed modes.
>>>
>>> Could you please provide suggestions for the speed mode in which tuning is
>>> not available?
>>>
>>
>> Normally the mmc host driver shouldn't have to care about what the
>> card supports, as that is the responsibility of the mmc core to
>> manage.
>>
>> The host should only need to implement the ->execute_tuning() ops,
>> which gets called when the card supports tuning (CMD19/21). Does it
>> make sense?
>>
> I think it is irrelevant to tuning procedure.
>
> Our host requires to adjust PHY setting after each time ios setting
> (SDCLK/bus width/speed mode) is changed.
> The simplified sequence is:
> mmc change ios --> mmc_set_ios() --> ->set_ios() --> after sdhci_set_ios(),
> adjust PHY setting.
> During PHY setting adjustment, out host driver has to send commands to
> test current sampling point. Tuning is another independent step.
For those speed modes (or other ios changes) that *don't* requires
tuning, then what will you do when you send the command to confirm the
change of PHY setting and it fails?
My assumption is that you will fail anyway, by propagating the error
to the mmc core. At least that what was my understanding from your
earlier replies, right!?
Then, I think there are no point having the host driver sending a
command to confirm the PHY settings, as the mmc core will anyway
discover if something goes wrong when the next command is sent.
Please correct me if I am wrong!
>
> Thus our host needs a valid command in PHY setting adjustment. Tuning command
> can be borrowed to complete this task in SD SDR50. But for other speed mode,
> we have to find out a valid command.
I thought we agreed on this wasn't necessary? Please see my upper response.
Kind regards
Uffe
^ permalink raw reply
* [PATCH] memory/atmel-ebi: Fix ns <-> cycles conversions
From: Boris Brezillon @ 2016-11-28 15:17 UTC (permalink / raw)
To: linux-arm-kernel
at91sam9_ebi_get_config() is incorrectly converting timings in clock
cycles into timings in nanoseconds by multiplying the cycle values by
the clk rate instead of the clk period.
at91sam9_ebi_xslate_config() has the same problem for the
tdf_ns -> tdf_cycles conversion.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Reported-by: Chris Leahy <leahycm@gmail.com>
Fixes: 6a4ec4cd0888 ("memory: add Atmel EBI (External Bus Interface) driver")
Cc: <stable@vger.kernel.org>
---
drivers/memory/atmel-ebi.c | 27 ++++++++++++++-------------
1 file changed, 14 insertions(+), 13 deletions(-)
diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c
index b5ed3bd082b5..e9ebc4f31d16 100644
--- a/drivers/memory/atmel-ebi.c
+++ b/drivers/memory/atmel-ebi.c
@@ -93,7 +93,7 @@ static void at91sam9_ebi_get_config(struct at91_ebi_dev *ebid,
struct at91_ebi_dev_config *conf)
{
struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9;
- unsigned int clk_rate = clk_get_rate(ebid->ebi->clk);
+ unsigned int clk_period = NSEC_PER_SEC / clk_get_rate(ebid->ebi->clk);
struct at91sam9_ebi_dev_config *config = &conf->sam9;
struct at91sam9_smc_timings *timings = &config->timings;
unsigned int val;
@@ -102,43 +102,43 @@ static void at91sam9_ebi_get_config(struct at91_ebi_dev *ebid,
config->mode = val & ~AT91_SMC_TDF;
val = (val & AT91_SMC_TDF) >> 16;
- timings->tdf_ns = clk_rate * val;
+ timings->tdf_ns = clk_period * val;
regmap_fields_read(fields->setup, conf->cs, &val);
timings->ncs_rd_setup_ns = (val >> 24) & 0x1f;
timings->ncs_rd_setup_ns += ((val >> 29) & 0x1) * 128;
- timings->ncs_rd_setup_ns *= clk_rate;
+ timings->ncs_rd_setup_ns *= clk_period;
timings->nrd_setup_ns = (val >> 16) & 0x1f;
timings->nrd_setup_ns += ((val >> 21) & 0x1) * 128;
- timings->nrd_setup_ns *= clk_rate;
+ timings->nrd_setup_ns *= clk_period;
timings->ncs_wr_setup_ns = (val >> 8) & 0x1f;
timings->ncs_wr_setup_ns += ((val >> 13) & 0x1) * 128;
- timings->ncs_wr_setup_ns *= clk_rate;
+ timings->ncs_wr_setup_ns *= clk_period;
timings->nwe_setup_ns = val & 0x1f;
timings->nwe_setup_ns += ((val >> 5) & 0x1) * 128;
- timings->nwe_setup_ns *= clk_rate;
+ timings->nwe_setup_ns *= clk_period;
regmap_fields_read(fields->pulse, conf->cs, &val);
timings->ncs_rd_pulse_ns = (val >> 24) & 0x3f;
timings->ncs_rd_pulse_ns += ((val >> 30) & 0x1) * 256;
- timings->ncs_rd_pulse_ns *= clk_rate;
+ timings->ncs_rd_pulse_ns *= clk_period;
timings->nrd_pulse_ns = (val >> 16) & 0x3f;
timings->nrd_pulse_ns += ((val >> 22) & 0x1) * 256;
- timings->nrd_pulse_ns *= clk_rate;
+ timings->nrd_pulse_ns *= clk_period;
timings->ncs_wr_pulse_ns = (val >> 8) & 0x3f;
timings->ncs_wr_pulse_ns += ((val >> 14) & 0x1) * 256;
- timings->ncs_wr_pulse_ns *= clk_rate;
+ timings->ncs_wr_pulse_ns *= clk_period;
timings->nwe_pulse_ns = val & 0x3f;
timings->nwe_pulse_ns += ((val >> 6) & 0x1) * 256;
- timings->nwe_pulse_ns *= clk_rate;
+ timings->nwe_pulse_ns *= clk_period;
regmap_fields_read(fields->cycle, conf->cs, &val);
timings->nrd_cycle_ns = (val >> 16) & 0x7f;
timings->nrd_cycle_ns += ((val >> 23) & 0x3) * 256;
- timings->nrd_cycle_ns *= clk_rate;
+ timings->nrd_cycle_ns *= clk_period;
timings->nwe_cycle_ns = val & 0x7f;
timings->nwe_cycle_ns += ((val >> 7) & 0x3) * 256;
- timings->nwe_cycle_ns *= clk_rate;
+ timings->nwe_cycle_ns *= clk_period;
}
static int at91_xlate_timing(struct device_node *np, const char *prop,
@@ -334,6 +334,7 @@ static int at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid,
struct at91_ebi_dev_config *conf)
{
unsigned int clk_rate = clk_get_rate(ebid->ebi->clk);
+ unsigned int clk_period = NSEC_PER_SEC / clk_rate;
struct at91sam9_ebi_dev_config *config = &conf->sam9;
struct at91sam9_smc_timings *timings = &config->timings;
struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9;
@@ -376,7 +377,7 @@ static int at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid,
val |= AT91SAM9_SMC_NWECYCLE(coded_val);
regmap_fields_write(fields->cycle, conf->cs, val);
- val = DIV_ROUND_UP(timings->tdf_ns, clk_rate);
+ val = DIV_ROUND_UP(timings->tdf_ns, clk_period);
if (val > AT91_SMC_TDF_MAX)
val = AT91_SMC_TDF_MAX;
regmap_fields_write(fields->mode, conf->cs,
--
2.7.4
^ permalink raw reply related
* arasan,sdhci.txt "compatibility" DT binding
From: Mason @ 2016-11-28 15:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <a20b2b4f-15d4-3c29-324c-1ced5dbcc67f@xilinx.com>
Hello,
@Shawn Lin, could you take a look below and tell me exactly
which IP core(s) Rockchip is using in its SoCs?
Based on the feedback I received, here is an updated list of
compatible strings and controller versions dealt with by the
drivers/mmc/host/sdhci-of-arasan.c code.
Xilinx Zynq:
"SD2.0 / SDIO2.0 / MMC3.31 AHB Host Controller"
"arasan,sdhci-8.9a"
NB: 8.9a is the documentation revision (dated 2011-10-19)
subsequent tweaks labeled 9.0a, 9.1a, 9.2a
Xilinx ZynqMP:
"SD3.0 / SDIO3.0 / eMMC4.51 AHB Host Controller"
"arasan,sdhci-8.9a"
NB: using the same compatible string as Zynq
Sigma SMP87xx
"SD3.0 / SDIO3.0 / eMMC4.4 AHB Host Controller"
no compatible string yet, platform-specific init required
APM:
"SD3.0 / SDIO3.0 / eMMC4.41 AHB Host Controller"
"arasan,sdhci-4.9a"
NB: 4.9a appears to be the documentation revision
no functional diff with "arasan,sdhci-8.9a"
Rockchip
Exact IP unknown, waiting for Shawn's answer
"arasan,sdhci-5.1"
NB: 5.1 appears to refer to the eMMC standard supported
On a final note, there are many variations of the Arasan IP.
I've tracked down at least the following:
SD_2.0_SDIO_2.0__MMC_3.31_AHB_Host_Controller.pdf
SD_3.0_SDIO_3.0_eMMC_4.41_OCP_Host_Controller.pdf
SD_3.0_SDIO_3.0_eMMC_4.4__AHB_Host_Controller.pdf
SD_3.0_SDIO_3.0_eMMC_4.51_Host_Controller.pdf
SD_3.0_SDIO_3.0_eMMC_4.5__Host_Controller.pdf
SD_4.1_SDIO_4.1_eMMC_4.51_Host_Controller.pdf
SD_4.1_SDIO_4.1_eMMC_5.1__Host_Controller.pdf
It seems to me the compatible string should specify
the SD/SDIO version AND the eMMC version, since it
seems many combinations are allowed, e.g. eMMC 4.51
has two possible SD versions.
What do you think?
Regards.
^ permalink raw reply
* [PATCH 2/2] net: dsa: mv88e6xxx: Add 88E6176 device tree support
From: Uwe Kleine-König @ 2016-11-28 15:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161128131735.GA4379@lunn.ch>
On 11/28/2016 02:17 PM, Andrew Lunn wrote:
>> I still wonder (and didn't get an answer back when I asked about this)
>> why a comment is preferred here. For other devices I know it's usual and
>> requested by the maintainers to use:
>>
>> compatible = "exact name", "earlyer device to match driver";
>>
>> . This is more robust, documents the situation more formally and makes
>> it better greppable. The price to pay is only a few bytes in the dtb
>> which IMO is ok.
>
> We did discuss this a while back. The information is useless and
> should to be ignored if present.
Who is "we"?
> The switch has a register which contains its model and revision. Each
> port has a set of registers, and register 3 contains the
> model/version. For all devices compatible with the 6085, the port
> registers start at address 0x10. For the 6190, the port registers
> start at 0x0. So given one of these two compatible strings, we can
> find the model of the device, from something which is burned into the
> silicon.
>
> Now, say we did add per device compatible strings. We look up the
> model burned into the silicon, find it is different to what the device
> tree is and do what? Fail the probe? Or just keep going using the
I'd say fail to probe is the right thing to do. Of course that doesn't
work for already supported models because it will break compatibility.
I'd value the advantages (i.e. easily find machines with a given
hardware) higher than making broken dtbs work, so being a bit silly is
fine for me.
> value in the silicon? It seems silly to fail the probe if the driver
> does support the model, but that means the device tree is never
> verified and hence probably wrong. Why have wrong information in the
> device tree, especially wrong information which we never use. It is
> better to not have that information in the device tree.
At least we'd have a canonical way to specify the type of switch. If
it's not verified it's as good and bad as a dts comment. But the latter
isn't available in the dtb, which I consider a small disadvantage.
Also it seems wrong to write "marvell,mv88e6085" (only) if I know the
hardware is really a "marvell,mv88e6176".
> Linus has said he does not like ARM devices because of all the busses
> which are not enumerable. Here we have a device which with a little
> bit of help we can enumerate. So we should.
If you write
compatible = "marvell,mv88e6176", "marvell,mv88e6085";
you can still enumerate in the same way as before.
There are several more instances where the device tree specifies
something that could be probed instead. Some examples:
compatible = "ethernet-phy-id0141.0DD1", "ethernet-phy-ieee802.3-c22";
compatible = "spansion,s25fl164k", "jedec,spi-nor";
compatible = "fsl,imx25-flexcan", "fsl,p1010-flexcan";
compatible = "arm,pl011", "arm,primecell";
So you think they are all doing it wrong?
Best regards
Uwe
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161128/42269bc2/attachment.sig>
^ permalink raw reply
* [PATCH] drm/atmel-hlcdc: Rework the fbdev creation logic
From: Daniel Vetter @ 2016-11-28 15:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480345267-18461-1-git-send-email-boris.brezillon@free-electrons.com>
On Mon, Nov 28, 2016 at 04:01:07PM +0100, Boris Brezillon wrote:
> Now that we wait for DRM panels to be available before registering the
> DRM device (returning -EPROBE_DEFER if the panel has not been probed
> yet), we no longer need to put the fbdev creation code in
> ->output_poll_changed().
>
> This removes the 10 secs delay between DRM dev registration and fbdev
> creation (polling period = 10 seconds).
>
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> Reported-by: Alex Vazquez <avazquez.dev@gmail.com>
+1 Would still be good to eventually resurrect Thierry's deferred fbdev
setup code for other use-cases. But for panels the driver should indeed
not need this.
-Daniel
> ---
> drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 18 +++++++-----------
> 1 file changed, 7 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> index 5f484310bee9..2325de7c5c6f 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
> @@ -431,15 +431,8 @@ static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
> {
> struct atmel_hlcdc_dc *dc = dev->dev_private;
>
> - if (dc->fbdev) {
> + if (dc->fbdev)
> drm_fbdev_cma_hotplug_event(dc->fbdev);
> - } else {
> - dc->fbdev = drm_fbdev_cma_init(dev, 24,
> - dev->mode_config.num_crtc,
> - dev->mode_config.num_connector);
> - if (IS_ERR(dc->fbdev))
> - dc->fbdev = NULL;
> - }
> }
>
> struct atmel_hlcdc_dc_commit {
> @@ -652,10 +645,13 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
>
> platform_set_drvdata(pdev, dev);
>
> - drm_kms_helper_poll_init(dev);
> + dc->fbdev = drm_fbdev_cma_init(dev, 24,
> + dev->mode_config.num_crtc,
> + dev->mode_config.num_connector);
> + if (IS_ERR(dc->fbdev))
> + dc->fbdev = NULL;
>
> - /* force connectors detection */
> - drm_helper_hpd_irq_event(dev);
> + drm_kms_helper_poll_init(dev);
>
> return 0;
>
> --
> 2.7.4
>
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
^ 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