Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6] KVM: arm/arm64: Route vtimer events to user space
From: Paolo Bonzini @ 2016-09-23 12:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474628854-69945-1-git-send-email-agraf@suse.de>



On 23/09/2016 13:07, Alexander Graf wrote:
> +		timer_ret = kvm_timer_sync_hwstate(vcpu);
>  
>  		kvm_vgic_sync_hwstate(vcpu);
>  
>  		preempt_enable();
>  
>  		ret = handle_exit(vcpu, run, ret);
> +
> +		if ((ret == 1) && timer_ret) {
> +			/*
> +			 * We have to exit straight away to ensure that we only
> +			 * ever notify user space once about a level change
> +			 */

Is this really a requirement?  It complicates the logic noticeably.

Paolo

> +			ret = -EINTR;
> +			run->exit_reason = KVM_EXIT_INTR;
> +		}
>  	}

^ permalink raw reply

* [PATCH] i2c: uniphier-f: fix misdetection of incomplete STOP condition
From: Masahiro Yamada @ 2016-09-23 12:04 UTC (permalink / raw)
  To: linux-arm-kernel

Currently, the status register FI2C_SR is checked immediately after
a STOP condition is issued in case of the deferred STOP condition.
It takes typically 5-10 usec until the corresponding bits in the
register are set, so the error check for "stop condition was not
completed" is very likely to be false positive.

Add wait code to relax the status register check.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

 drivers/i2c/busses/i2c-uniphier-f.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c
index 829df91..e5886eb 100644
--- a/drivers/i2c/busses/i2c-uniphier-f.c
+++ b/drivers/i2c/busses/i2c-uniphier-f.c
@@ -13,6 +13,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -348,10 +349,19 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
 	dev_dbg(&adap->dev, "complete\n");
 
 	if (unlikely(priv->flags & UNIPHIER_FI2C_DEFER_STOP_COMP)) {
-		u32 status = readl(priv->membase + UNIPHIER_FI2C_SR);
+		time_left = 20;
 
-		if (!(status & UNIPHIER_FI2C_SR_STS) ||
-		    status & UNIPHIER_FI2C_SR_BB) {
+		while (--time_left) {
+			u32 status = readl(priv->membase + UNIPHIER_FI2C_SR);
+
+			if ((status & UNIPHIER_FI2C_SR_STS) &&
+			    !(status & UNIPHIER_FI2C_SR_BB))
+				break;
+
+			udelay(1);
+		}
+
+		if (!time_left) {
 			dev_err(&adap->dev,
 				"stop condition was not completed.\n");
 			uniphier_fi2c_recover(priv);
-- 
1.9.1

^ permalink raw reply related

* [PATCH 2/2] armv8: aarch32: Add SMP support for 32-bit Linux kernel
From: Arnd Bergmann @ 2016-09-23 11:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474597146-33312-2-git-send-email-b18965@freescale.com>

On Friday, September 23, 2016 10:19:06 AM CEST Alison Wang wrote:
> The patch adds SMP support for running 32-bit Linux kernel for
> Layerscape platforms. Spin-table method is used for SMP support.
> 
> Signed-off-by: Alison Wang <alison.wang@nxp.com>
> Signed-off-by: Chenhui Zhao <chenhui.zhao@nxp.com>
> ---
>  arch/arm/mach-imx/common.h          |  1 +
>  arch/arm/mach-imx/mach-layerscape.c |  1 +
>  arch/arm/mach-imx/platsmp.c         | 49 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 51 insertions(+)
> 

This really shouldn't be needed: if it works on a 64-bit kernel
without platform specific SMP operations, why do you need it on 32-bit
kernels?

	Arnd

^ permalink raw reply

* [PATCH V3 RFT 3/5] ARM64: dts: bcm283x: Use dtsi for USB host mode
From: Eric Anholt @ 2016-09-23 11:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4cda98b0-03a6-f43d-0eee-29b7702eac8c@i2se.com>

Stefan Wahren <stefan.wahren@i2se.com> writes:

> Am 23.09.2016 um 10:15 schrieb Eric Anholt:
>> Stefan Wahren <stefan.wahren@i2se.com> writes:
>>
>>> Am 07.09.2016 um 16:17 schrieb Gerd Hoffmann:
>>>>   Hi,
>>>>
>>>>> ARM: dts: Remove use of skeleton.dtsi from bcm283x.dtsi
>>>>> ARM64: dts: bcm: Use a symlink to R-Pi dtsi files from arch=arm
>>>> Picked them up.
>>>>
>>>>> In case of a multiplatform config with following settings the driver
>>>>> isn't able to "detect" the role:
>>>>>
>>>>> CONFIG_USB=y
>>>>> CONFIG_USB_OTG=y
>>>>> CONFIG_USB_DWC2=y
>>>>> CONFIG_USB_DWC2_DUAL_ROLE=y
>>>>> CONFIG_USB_PHY=y
>>>>> CONFIG_NOP_USB_XCEIV=y
>>>>> CONFIG_USB_GADGET=y
>>>>> CONFIG_USB_ZERO=y
>>>> Configured my kernel that way so the test actually tests something ;)
>>>>
>>>> And ... everything looks fine.  usb ethernet works, and so does the usb
>>>> keyboard.  /proc/device-tree/soc/usb at 7e980000/dr_mode exists and
>>>> contains "host".
>>>>
>>>> Tested-by: Gerd Hoffmann <kraxel@redhat.com>
>>>>
>>> This patch hasn't been merged yet. Is there something wrong?
>> I hadn't bumped it back to todo from RFT.  Also, is this a priority
>> patch, since submitting merges is a pain? 
> No
>> Does USB break in some way
>> without it?
>
> No. I only want know if this is still in queue and does someone care
> about this patch.

It's in my queue now.  Sorry for missing it!
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 800 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160923/03281913/attachment.sig>

^ permalink raw reply

* [PATCH 2/2] drm: zte: add initial vou drm driver
From: Daniel Vetter @ 2016-09-23 11:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474615449-16893-2-git-send-email-shawn.guo@linaro.org>

Please resubmit with dri-devel on cc.

Thanks, Daniel

On Fri, Sep 23, 2016 at 9:24 AM, Shawn Guo <shawn.guo@linaro.org> wrote:
> It adds the initial ZTE VOU display controller DRM driver.  There are
> still some features to be added, like overlay plane, scaling, and more
> output devices support.  But it's already useful with dual CRTCs and
> HDMI monitor working.
>
> It's been tested on Debian Jessie LXDE desktop with modesetting driver.
>
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> ---
>  drivers/gpu/drm/Kconfig          |   2 +
>  drivers/gpu/drm/Makefile         |   1 +
>  drivers/gpu/drm/zte/Kconfig      |   8 +
>  drivers/gpu/drm/zte/Makefile     |   8 +
>  drivers/gpu/drm/zte/zx_crtc.c    | 711 +++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/zte/zx_crtc.h    |  47 +++
>  drivers/gpu/drm/zte/zx_drm_drv.c | 258 ++++++++++++++
>  drivers/gpu/drm/zte/zx_drm_drv.h |  22 ++
>  drivers/gpu/drm/zte/zx_hdmi.c    | 540 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/zte/zx_plane.c   | 362 ++++++++++++++++++++
>  drivers/gpu/drm/zte/zx_plane.h   |  26 ++
>  11 files changed, 1985 insertions(+)
>  create mode 100644 drivers/gpu/drm/zte/Kconfig
>  create mode 100644 drivers/gpu/drm/zte/Makefile
>  create mode 100644 drivers/gpu/drm/zte/zx_crtc.c
>  create mode 100644 drivers/gpu/drm/zte/zx_crtc.h
>  create mode 100644 drivers/gpu/drm/zte/zx_drm_drv.c
>  create mode 100644 drivers/gpu/drm/zte/zx_drm_drv.h
>  create mode 100644 drivers/gpu/drm/zte/zx_hdmi.c
>  create mode 100644 drivers/gpu/drm/zte/zx_plane.c
>  create mode 100644 drivers/gpu/drm/zte/zx_plane.h
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 483059a22b1b..a91f8cecbe0f 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -223,6 +223,8 @@ source "drivers/gpu/drm/hisilicon/Kconfig"
>
>  source "drivers/gpu/drm/mediatek/Kconfig"
>
> +source "drivers/gpu/drm/zte/Kconfig"
> +
>  # Keep legacy drivers last
>
>  menuconfig DRM_LEGACY
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 439d89b25ae0..fe461c94d266 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -85,3 +85,4 @@ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
>  obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
>  obj-$(CONFIG_DRM_ARCPGU)+= arc/
>  obj-y                  += hisilicon/
> +obj-$(CONFIG_DRM_ZTE)  += zte/
> diff --git a/drivers/gpu/drm/zte/Kconfig b/drivers/gpu/drm/zte/Kconfig
> new file mode 100644
> index 000000000000..4065b2840f1c
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/Kconfig
> @@ -0,0 +1,8 @@
> +config DRM_ZTE
> +       tristate "DRM Support for ZTE SoCs"
> +       depends on DRM && ARCH_ZX
> +       select DRM_KMS_CMA_HELPER
> +       select DRM_KMS_FB_HELPER
> +       select DRM_KMS_HELPER
> +       help
> +         Choose this option to enable DRM on ZTE ZX SoCs.
> diff --git a/drivers/gpu/drm/zte/Makefile b/drivers/gpu/drm/zte/Makefile
> new file mode 100644
> index 000000000000..b40968dc749f
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/Makefile
> @@ -0,0 +1,8 @@
> +zxdrm-y := \
> +       zx_drm_drv.o \
> +       zx_crtc.o \
> +       zx_plane.o \
> +       zx_hdmi.o
> +
> +obj-$(CONFIG_DRM_ZTE) += zxdrm.o
> +
> diff --git a/drivers/gpu/drm/zte/zx_crtc.c b/drivers/gpu/drm/zte/zx_crtc.c
> new file mode 100644
> index 000000000000..531541482e1f
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_crtc.c
> @@ -0,0 +1,711 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_plane_helper.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/of_address.h>
> +#include <video/videomode.h>
> +
> +#include "zx_drm_drv.h"
> +#include "zx_crtc.h"
> +#include "zx_plane.h"
> +
> +/* OSD (GPC_GLOBAL) registers */
> +#define OSD_INT_STA                    0x04
> +#define OSD_INT_CLRSTA                 0x08
> +#define OSD_INT_MSK                    0x0c
> +#define OSD_INT_AUX_UPT                        BIT(14)
> +#define OSD_INT_MAIN_UPT               BIT(13)
> +#define OSD_INT_GL1_LBW                        BIT(10)
> +#define OSD_INT_GL0_LBW                        BIT(9)
> +#define OSD_INT_VL2_LBW                        BIT(8)
> +#define OSD_INT_VL1_LBW                        BIT(7)
> +#define OSD_INT_VL0_LBW                        BIT(6)
> +#define OSD_INT_BUS_ERR                        BIT(3)
> +#define OSD_INT_CFG_ERR                        BIT(2)
> +#define OSD_INT_ERROR (\
> +       OSD_INT_GL1_LBW | OSD_INT_GL0_LBW | \
> +       OSD_INT_VL2_LBW | OSD_INT_VL1_LBW | OSD_INT_VL0_LBW | \
> +       OSD_INT_BUS_ERR | OSD_INT_CFG_ERR \
> +)
> +#define OSD_INT_ENABLE (OSD_INT_ERROR | OSD_INT_AUX_UPT | OSD_INT_MAIN_UPT)
> +#define OSD_CTRL0                      0x10
> +#define OSD_CTRL0_GL0_EN               BIT(7)
> +#define OSD_CTRL0_GL0_SEL              BIT(6)
> +#define OSD_CTRL0_GL1_EN               BIT(5)
> +#define OSD_CTRL0_GL1_SEL              BIT(4)
> +#define OSD_RST_CLR                    0x1c
> +#define RST_PER_FRAME                  BIT(19)
> +
> +/* Main/Aux channel registers */
> +#define OSD_MAIN_CHN                   0x470
> +#define OSD_AUX_CHN                    0x4d0
> +#define CHN_CTRL0                      0x00
> +#define CHN_ENABLE                     BIT(0)
> +#define CHN_CTRL1                      0x04
> +#define CHN_SCREEN_W_SHIFT             18
> +#define CHN_SCREEN_W_MASK              (0x1fff << CHN_SCREEN_W_SHIFT)
> +#define CHN_SCREEN_H_SHIFT             5
> +#define CHN_SCREEN_H_MASK              (0x1fff << CHN_SCREEN_H_SHIFT)
> +#define CHN_UPDATE                     0x08
> +
> +/* TIMING_CTRL registers */
> +#define VOU_TIMING_CTRL                        0x1000
> +#define TIMING_TC_ENABLE               0x04
> +#define AUX_TC_EN                      BIT(1)
> +#define MAIN_TC_EN                     BIT(0)
> +#define FIR_MAIN_ACTIVE                        0x08
> +#define FIR_AUX_ACTIVE                 0x0c
> +#define FIR_MAIN_H_TIMING              0x10
> +#define FIR_MAIN_V_TIMING              0x14
> +#define FIR_AUX_H_TIMING               0x18
> +#define FIR_AUX_V_TIMING               0x1c
> +#define SYNC_WIDE_SHIFT                        22
> +#define SYNC_WIDE_MASK                 (0x3ff << SYNC_WIDE_SHIFT)
> +#define BACK_PORCH_SHIFT               11
> +#define BACK_PORCH_MASK                        (0x7ff << BACK_PORCH_SHIFT)
> +#define FRONT_PORCH_SHIFT              0
> +#define FRONT_PORCH_MASK               (0x7ff << FRONT_PORCH_SHIFT)
> +#define TIMING_CTRL                    0x20
> +#define AUX_POL_SHIFT                  3
> +#define AUX_POL_MASK                   (0x7 << AUX_POL_SHIFT)
> +#define MAIN_POL_SHIFT                 0
> +#define MAIN_POL_MASK                  (0x7 << MAIN_POL_SHIFT)
> +#define POL_DE_SHIFT                   2
> +#define POL_VSYNC_SHIFT                        1
> +#define POL_HSYNC_SHIFT                        0
> +#define TIMING_INT_CTRL                        0x24
> +#define TIMING_INT_STATE               0x28
> +#define TIMING_INT_AUX_FRAME           BIT(3)
> +#define TIMING_INT_MAIN_FRAME          BIT(1)
> +#define TIMING_INT_AUX_FRAME_SEL_VSW   (0x2 << 10)
> +#define TIMING_INT_MAIN_FRAME_SEL_VSW  (0x2 << 6)
> +#define TIMING_INT_ENABLE (\
> +       TIMING_INT_MAIN_FRAME_SEL_VSW | TIMING_INT_AUX_FRAME_SEL_VSW | \
> +       TIMING_INT_MAIN_FRAME | TIMING_INT_AUX_FRAME \
> +)
> +#define TIMING_MAIN_SHIFT              0x2c
> +#define TIMING_AUX_SHIFT               0x30
> +#define H_SHIFT_VAL                    0x0048
> +#define TIMING_MAIN_PI_SHIFT           0x68
> +#define TIMING_AUX_PI_SHIFT            0x6c
> +#define H_PI_SHIFT_VAL                 0x000f
> +
> +/* DTRC registers */
> +#define DTRC                           0x5000
> +#define DTRC_F0_CTRL                   0x2c
> +#define DTRC_F1_CTRL                   0x5c
> +#define DTRC_DECOMPRESS_BYPASS         BIT(17)
> +#define DTRC_DETILE_CTRL               0x68
> +#define TILE2RASTESCAN_BYPASS_MODE     BIT(30)
> +#define DETILE_ARIDR_MODE_MASK         (0x3 << 0)
> +#define DETILE_ARID_ALL                        0
> +#define DETILE_ARID_IN_ARIDR           1
> +#define DETILE_ARID_BYP_BUT_ARIDR      2
> +#define DETILE_ARID_IN_ARIDR2          3
> +#define DTRC_ARID                      0x6c
> +#define DTRC_DEC2DDR_ARID              0x70
> +
> +/* VOU_CTRL registers */
> +#define VOU_CTRL                       0x6000
> +#define VOU_INF_EN                     0x00
> +#define VOU_INF_CH_SEL                 0x04
> +#define VOU_INF_DATA_SEL               0x08
> +#define VOU_SOFT_RST                   0x14
> +#define VOU_CLK_SEL                    0x18
> +#define VOU_CLK_GL1_SEL                        BIT(5)
> +#define VOU_CLK_GL0_SEL                        BIT(4)
> +#define VOU_CLK_REQEN                  0x20
> +#define VOU_CLK_EN                     0x24
> +
> +/* OTFPPU_CTRL registers */
> +#define OTFPPU_CTRL                    0xa000
> +#define OTFPPU_RSZ_DATA_SOURCE         0x04
> +
> +#define GL_NUM                         2
> +#define VL_NUM                         3
> +
> +enum vou_chn_type {
> +       VOU_CHN_MAIN,
> +       VOU_CHN_AUX,
> +};
> +
> +struct zx_crtc {
> +       struct drm_crtc crtc;
> +       struct drm_plane *primary;
> +       struct device *dev;
> +       void __iomem *chnreg;
> +       enum vou_chn_type chn_type;
> +       struct clk *pixclk;
> +};
> +
> +#define to_zx_crtc(x)  container_of(crtc, struct zx_crtc, crtc)
> +
> +struct zx_vou {
> +       struct device *dev;
> +       void __iomem *mmio;     /* Same as OSD address */
> +       void __iomem *timing;
> +       void __iomem *vouctl;
> +       void __iomem *otfppu;
> +       void __iomem *dtrc;
> +       struct clk *axi_clk;
> +       struct clk *ppu_clk;
> +       struct clk *main_clk;
> +       struct clk *aux_clk;
> +       struct zx_crtc *main_crtc;
> +       struct zx_crtc *aux_crtc;
> +};
> +
> +struct zx_layer_data gl_data[GL_NUM] = {
> +       {
> +               .layer_offset = 0x130,
> +               .csc_offset = 0x580,
> +               .hbsc_offset = 0x820,
> +               .rsz_offset = 0xa600,
> +       }, {
> +               .layer_offset = 0x200,
> +               .csc_offset = 0x5d0,
> +               .hbsc_offset = 0x860,
> +               .rsz_offset = 0xa800,
> +       },
> +};
> +
> +/* Video layers have no CSC block */
> +struct zx_layer_data vl_data[VL_NUM] = {
> +       {
> +               .layer_offset = 0x040,
> +               .hbsc_offset = 0x760,
> +               .rsz_offset = 0xaa00,
> +       }, {
> +               .layer_offset = 0x090,
> +               .hbsc_offset = 0x7a0,
> +               .rsz_offset = 0xac00,
> +       }, {
> +               .layer_offset = 0x0e0,
> +               .hbsc_offset = 0x7e0,
> +               .rsz_offset = 0xae00,
> +       },
> +};
> +
> +static inline bool is_main_crtc(struct drm_crtc *crtc)
> +{
> +       struct zx_crtc *zcrtc = to_zx_crtc(crtc);
> +
> +       return zcrtc->chn_type == VOU_CHN_MAIN;
> +}
> +
> +void vou_inf_enable(struct vou_inf *inf)
> +{
> +       struct drm_encoder *encoder = inf->encoder;
> +       struct drm_device *drm = encoder->dev;
> +       struct zx_drm_private *priv = drm->dev_private;
> +       struct zx_vou *vou = priv->vou;
> +       bool is_main = is_main_crtc(encoder->crtc);
> +       u32 data_sel_shift = inf->id << 1;
> +       u32 val;
> +
> +       /* Select data format */
> +       val = readl(vou->vouctl + VOU_INF_DATA_SEL);
> +       val &= ~(0x3 << data_sel_shift);
> +       val |= inf->data_sel << data_sel_shift;
> +       writel(val, vou->vouctl + VOU_INF_DATA_SEL);
> +
> +       /* Select channel */
> +       val = readl(vou->vouctl + VOU_INF_CH_SEL);
> +       if (is_main)
> +               val &= ~(1 << inf->id);
> +       else
> +               val |= 1 << inf->id;
> +       writel(val, vou->vouctl + VOU_INF_CH_SEL);
> +
> +       /* Select interface clocks */
> +       val = readl(vou->vouctl + VOU_CLK_SEL);
> +       if (is_main)
> +               val &= ~inf->clocks_sel_bits;
> +       else
> +               val |= inf->clocks_sel_bits;
> +       writel(val, vou->vouctl + VOU_CLK_SEL);
> +
> +       /* Enable interface clocks */
> +       val = readl(vou->vouctl + VOU_CLK_EN);
> +       val |= inf->clocks_en_bits;
> +       writel(val, vou->vouctl + VOU_CLK_EN);
> +
> +       /* Enable the device */
> +       val = readl(vou->vouctl + VOU_INF_EN);
> +       val |= 1 << inf->id;
> +       writel(val, vou->vouctl + VOU_INF_EN);
> +}
> +
> +void vou_inf_disable(struct vou_inf *inf)
> +{
> +       struct drm_encoder *encoder = inf->encoder;
> +       struct drm_device *drm = encoder->dev;
> +       struct zx_drm_private *priv = drm->dev_private;
> +       struct zx_vou *vou = priv->vou;
> +       u32 val;
> +
> +       /* Disable the device */
> +       val = readl(vou->vouctl + VOU_INF_EN);
> +       val &= ~(1 << inf->id);
> +       writel(val, vou->vouctl + VOU_INF_EN);
> +
> +       /* Disable interface clocks */
> +       val = readl(vou->vouctl + VOU_CLK_EN);
> +       val &= ~inf->clocks_en_bits;
> +       writel(val, vou->vouctl + VOU_CLK_EN);
> +}
> +
> +static inline void vou_chn_set_update(struct zx_crtc *zcrtc)
> +{
> +       writel(1, zcrtc->chnreg + CHN_UPDATE);
> +}
> +
> +static void zx_crtc_enable(struct drm_crtc *crtc)
> +{
> +       struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +       struct zx_crtc *zcrtc = to_zx_crtc(crtc);
> +       struct zx_vou *vou = dev_get_drvdata(zcrtc->dev);
> +       bool is_main = is_main_crtc(crtc);
> +       struct videomode vm;
> +       u32 pol = 0;
> +       u32 val;
> +
> +       drm_display_mode_to_videomode(mode, &vm);
> +
> +       /* Set up timing parameters */
> +       val = (vm.vactive - 1) << 16;
> +       val |= (vm.hactive - 1) & 0xffff;
> +       writel(val, vou->timing + (is_main ? FIR_MAIN_ACTIVE : FIR_AUX_ACTIVE));
> +
> +       val = ((vm.hsync_len - 1) << SYNC_WIDE_SHIFT) & SYNC_WIDE_MASK;
> +       val |= ((vm.hback_porch - 1) << BACK_PORCH_SHIFT) & BACK_PORCH_MASK;
> +       val |= ((vm.hfront_porch - 1) << FRONT_PORCH_SHIFT) & FRONT_PORCH_MASK;
> +       writel(val, vou->timing + (is_main ? FIR_MAIN_H_TIMING :
> +                                            FIR_AUX_H_TIMING));
> +
> +       val = ((vm.vsync_len - 1) << SYNC_WIDE_SHIFT) & SYNC_WIDE_MASK;
> +       val |= ((vm.vback_porch - 1) << BACK_PORCH_SHIFT) & BACK_PORCH_MASK;
> +       val |= ((vm.vfront_porch - 1) << FRONT_PORCH_SHIFT) & FRONT_PORCH_MASK;
> +       writel(val, vou->timing + (is_main ? FIR_MAIN_V_TIMING :
> +                                            FIR_AUX_V_TIMING));
> +
> +       /* Set up polarities */
> +       if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW)
> +               pol |= 1 << POL_VSYNC_SHIFT;
> +       if (vm.flags & DISPLAY_FLAGS_HSYNC_LOW)
> +               pol |= 1 << POL_HSYNC_SHIFT;
> +
> +       val = readl(vou->timing + TIMING_CTRL);
> +       val &= ~(is_main ? MAIN_POL_MASK : AUX_POL_MASK);
> +       val |= pol << (is_main ? MAIN_POL_SHIFT : AUX_POL_SHIFT);
> +       writel(val, vou->timing + TIMING_CTRL);
> +
> +       /* Setup SHIFT register by following what ZTE BSP does */
> +       writel(H_SHIFT_VAL, vou->timing + (is_main ? TIMING_MAIN_SHIFT :
> +                                                    TIMING_AUX_SHIFT));
> +       writel(H_PI_SHIFT_VAL, vou->timing + (is_main ? TIMING_MAIN_PI_SHIFT :
> +                                                       TIMING_AUX_PI_SHIFT));
> +
> +       /* Enable TIMING_CTRL */
> +       val = readl(vou->timing + TIMING_TC_ENABLE);
> +       val |= is_main ? MAIN_TC_EN : AUX_TC_EN;
> +       writel(val, vou->timing + TIMING_TC_ENABLE);
> +
> +       /* Configure channel screen size */
> +       val = readl(zcrtc->chnreg + CHN_CTRL1);
> +       val &= ~(CHN_SCREEN_W_MASK | CHN_SCREEN_H_MASK);
> +       val |= (vm.hactive << CHN_SCREEN_W_SHIFT) & CHN_SCREEN_W_MASK;
> +       val |= (vm.vactive << CHN_SCREEN_H_SHIFT) & CHN_SCREEN_H_MASK;
> +       writel(val, zcrtc->chnreg + CHN_CTRL1);
> +
> +       /* Update channel */
> +       vou_chn_set_update(zcrtc);
> +
> +       /* Enable channel */
> +       val = readl(zcrtc->chnreg + CHN_CTRL0);
> +       val |= CHN_ENABLE;
> +       writel(val, zcrtc->chnreg + CHN_CTRL0);
> +
> +       /* Enable Graphic Layer */
> +       val = readl(vou->mmio + OSD_CTRL0);
> +       val |= is_main ? OSD_CTRL0_GL0_EN : OSD_CTRL0_GL1_EN;
> +       writel(val, vou->mmio + OSD_CTRL0);
> +
> +       drm_crtc_vblank_on(crtc);
> +
> +       /* Enable pixel clock */
> +       clk_set_rate(zcrtc->pixclk, mode->clock * 1000);
> +       clk_prepare_enable(zcrtc->pixclk);
> +}
> +
> +static void zx_crtc_disable(struct drm_crtc *crtc)
> +{
> +       struct zx_crtc *zcrtc = to_zx_crtc(crtc);
> +       struct zx_vou *vou = dev_get_drvdata(zcrtc->dev);
> +       bool is_main = is_main_crtc(crtc);
> +       u32 val;
> +
> +       clk_disable_unprepare(zcrtc->pixclk);
> +
> +       drm_crtc_vblank_off(crtc);
> +
> +       /* Disable Graphic Layer */
> +       val = readl(vou->mmio + OSD_CTRL0);
> +       val &= ~(is_main ? OSD_CTRL0_GL0_EN : OSD_CTRL0_GL1_EN);
> +       writel(val, vou->mmio + OSD_CTRL0);
> +
> +       /* Disable channel */
> +       val = readl(zcrtc->chnreg + CHN_CTRL0);
> +       val &= ~CHN_ENABLE;
> +       writel(val, zcrtc->chnreg + CHN_CTRL0);
> +
> +       /* Disable TIMING_CTRL */
> +       val = readl(vou->timing + TIMING_TC_ENABLE);
> +       val &= ~(is_main ? MAIN_TC_EN : AUX_TC_EN);
> +       writel(val, vou->timing + TIMING_TC_ENABLE);
> +}
> +
> +static void zx_crtc_atomic_begin(struct drm_crtc *crtc,
> +                                struct drm_crtc_state *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 const struct drm_crtc_helper_funcs zx_crtc_helper_funcs = {
> +       .enable = zx_crtc_enable,
> +       .disable = zx_crtc_disable,
> +       .atomic_begin = zx_crtc_atomic_begin,
> +};
> +
> +static const struct drm_crtc_funcs zx_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 struct zx_crtc *zx_crtc_init(struct drm_device *drm,
> +                                   enum vou_chn_type chn_type)
> +{
> +       struct zx_drm_private *priv = drm->dev_private;
> +       struct zx_vou *vou = priv->vou;
> +       struct device *dev = vou->dev;
> +       struct zx_layer_data *data;
> +       struct zx_crtc *zcrtc;
> +       int ret;
> +
> +       zcrtc = devm_kzalloc(dev, sizeof(*zcrtc), GFP_KERNEL);
> +       if (!zcrtc)
> +               return ERR_PTR(-ENOMEM);
> +
> +       zcrtc->dev = dev;
> +       zcrtc->chn_type = chn_type;
> +
> +       if (chn_type == VOU_CHN_MAIN) {
> +               data = &gl_data[0];
> +               zcrtc->chnreg = vou->mmio + OSD_MAIN_CHN;
> +       } else {
> +               data = &gl_data[1];
> +               zcrtc->chnreg = vou->mmio + OSD_AUX_CHN;
> +       }
> +
> +       zcrtc->pixclk = devm_clk_get(dev, (chn_type == VOU_CHN_MAIN) ?
> +                                         "main_wclk" : "aux_wclk");
> +       if (IS_ERR(zcrtc->pixclk))
> +               return ERR_PTR(PTR_ERR(zcrtc->pixclk));
> +
> +       zcrtc->primary = zx_plane_init(drm, dev, vou->mmio, data,
> +                                      DRM_PLANE_TYPE_PRIMARY);
> +       if (IS_ERR(zcrtc->primary))
> +               return ERR_PTR(PTR_ERR(zcrtc->primary));
> +
> +       ret = drm_crtc_init_with_planes(drm, &zcrtc->crtc, zcrtc->primary, NULL,
> +                                       &zx_crtc_funcs, NULL);
> +       if (ret)
> +               return ERR_PTR(ret);
> +
> +       drm_crtc_helper_add(&zcrtc->crtc, &zx_crtc_helper_funcs);
> +
> +       return zcrtc;
> +}
> +
> +int zx_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe)
> +{
> +       struct zx_drm_private *priv = drm->dev_private;
> +       struct zx_vou *vou = priv->vou;
> +       u32 intctl;
> +
> +       intctl = readl(vou->timing + TIMING_INT_CTRL);
> +       if (pipe == 0)
> +               intctl |= TIMING_INT_MAIN_FRAME;
> +       else
> +               intctl |= TIMING_INT_AUX_FRAME;
> +       writel(intctl, vou->timing + TIMING_INT_CTRL);
> +
> +       return 0;
> +}
> +
> +void zx_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe)
> +{
> +       struct zx_drm_private *priv = drm->dev_private;
> +       struct zx_vou *vou = priv->vou;
> +       u32 intctl;
> +
> +       intctl = readl(vou->timing + TIMING_INT_CTRL);
> +       if (pipe == 0)
> +               intctl &= ~TIMING_INT_MAIN_FRAME;
> +       else
> +               intctl &= ~TIMING_INT_AUX_FRAME;
> +       writel(intctl, vou->timing + TIMING_INT_CTRL);
> +}
> +
> +static irqreturn_t vou_irq_handler(int irq, void *dev_id)
> +{
> +       struct zx_vou *vou = dev_id;
> +       u32 state;
> +
> +       /* Handle TIMING_CTRL frame interrupts */
> +       state = readl(vou->timing + TIMING_INT_STATE);
> +       writel(state, vou->timing + TIMING_INT_STATE);
> +
> +       if (state & TIMING_INT_MAIN_FRAME)
> +               drm_crtc_handle_vblank(&vou->main_crtc->crtc);
> +
> +       if (state & TIMING_INT_AUX_FRAME)
> +               drm_crtc_handle_vblank(&vou->aux_crtc->crtc);
> +
> +       /* Handle OSD interrupts */
> +       state = readl(vou->mmio + OSD_INT_STA);
> +       writel(state, vou->mmio + OSD_INT_CLRSTA);
> +
> +       if (state & OSD_INT_MAIN_UPT) {
> +               vou_chn_set_update(vou->main_crtc);
> +               zx_plane_set_update(vou->main_crtc->primary);
> +       }
> +
> +       if (state & OSD_INT_AUX_UPT) {
> +               vou_chn_set_update(vou->aux_crtc);
> +               zx_plane_set_update(vou->aux_crtc->primary);
> +       }
> +
> +       if (state & OSD_INT_ERROR)
> +               dev_err(vou->dev, "OSD ERROR: 0x%08x!\n", state);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static void vou_dtrc_init(struct zx_vou *vou)
> +{
> +       u32 val;
> +
> +       val = readl(vou->dtrc + DTRC_DETILE_CTRL);
> +       /* Clear bit for bypass by ID */
> +       val &= ~TILE2RASTESCAN_BYPASS_MODE;
> +       /* Select ARIDR mode */
> +       val &= ~DETILE_ARIDR_MODE_MASK;
> +       val |= DETILE_ARID_IN_ARIDR;
> +       writel(val, vou->dtrc + DTRC_DETILE_CTRL);
> +
> +       /* Bypass decompression for both frames */
> +       val = readl(vou->dtrc + DTRC_F0_CTRL);
> +       val |= DTRC_DECOMPRESS_BYPASS;
> +       writel(val, vou->dtrc + DTRC_F0_CTRL);
> +
> +       val = readl(vou->dtrc + DTRC_F1_CTRL);
> +       val |= DTRC_DECOMPRESS_BYPASS;
> +       writel(val, vou->dtrc + DTRC_F1_CTRL);
> +
> +       /* Set up ARID register */
> +       val = 0x0e;
> +       val |= 0x0f << 8;
> +       val |= 0x0e << 16;
> +       val |= 0x0f << 24;
> +       writel(val, vou->dtrc + DTRC_ARID);
> +
> +       /* Set up DEC2DDR_ARID register */
> +       val = 0x0e;
> +       val |= 0x0f << 8;
> +       writel(val, vou->dtrc + DTRC_DEC2DDR_ARID);
> +}
> +
> +static void vou_hw_init(struct zx_vou *vou)
> +{
> +       u32 val;
> +
> +       /* Set GL0 to main channel and GL1 to aux channel */
> +       val = readl(vou->mmio + OSD_CTRL0);
> +       val &= ~OSD_CTRL0_GL0_SEL;
> +       val |= OSD_CTRL0_GL1_SEL;
> +       writel(val, vou->mmio + OSD_CTRL0);
> +
> +       /* Release reset for all VOU modules */
> +       writel(~0, vou->vouctl + VOU_SOFT_RST);
> +
> +       /* Select main clock for GL0 and aux clock for GL1 module */
> +       val = readl(vou->vouctl + VOU_CLK_SEL);
> +       val &= ~VOU_CLK_GL0_SEL;
> +       val |= VOU_CLK_GL1_SEL;
> +       writel(val, vou->vouctl + VOU_CLK_SEL);
> +
> +       /* Enable clock auto-gating for all VOU modules */
> +       writel(~0, vou->vouctl + VOU_CLK_REQEN);
> +
> +       /* Enable all VOU module clocks */
> +       writel(~0, vou->vouctl + VOU_CLK_EN);
> +
> +       /* Clear both OSD and TIMING_CTRL interrupt state */
> +       writel(~0, vou->mmio + OSD_INT_CLRSTA);
> +       writel(~0, vou->timing + TIMING_INT_STATE);
> +
> +       /* Enable OSD and TIMING_CTRL interrrupts */
> +       writel(OSD_INT_ENABLE, vou->mmio + OSD_INT_MSK);
> +       writel(TIMING_INT_ENABLE, vou->timing + TIMING_INT_CTRL);
> +
> +       /* Select GPC as input to gl/vl scaler as a sane default setting */
> +       writel(0x2a, vou->otfppu + OTFPPU_RSZ_DATA_SOURCE);
> +
> +       /*
> +        * Needs to reset channel and layer logic per frame when frame starts
> +        * to get VOU work properly.
> +        */
> +       val = readl(vou->mmio + OSD_RST_CLR);
> +       val |= RST_PER_FRAME;
> +       writel(val, vou->mmio + OSD_RST_CLR);
> +
> +       vou_dtrc_init(vou);
> +}
> +
> +static int zx_crtc_bind(struct device *dev, struct device *master, void *data)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct device_node *np = dev->of_node;
> +       struct drm_device *drm = data;
> +       struct zx_drm_private *priv = drm->dev_private;
> +       struct resource res;
> +       struct zx_vou *vou;
> +       int irq;
> +       int ret;
> +
> +       vou = devm_kzalloc(dev, sizeof(*vou), GFP_KERNEL);
> +       if (!vou)
> +               return -ENOMEM;
> +
> +       /*
> +        * Skip mem region request to avoid overlapping on output device
> +        * mem regions, as the regions will be requested by connector drivers.
> +        */
> +       of_address_to_resource(np, 0, &res);
> +       vou->mmio = devm_ioremap(dev, res.start, resource_size(&res));
> +       if (!vou->mmio)
> +               return -ENOMEM;
> +
> +       vou->timing = vou->mmio + VOU_TIMING_CTRL;
> +       vou->vouctl = vou->mmio + VOU_CTRL;
> +       vou->otfppu = vou->mmio + OTFPPU_CTRL;
> +       vou->dtrc = vou->mmio + DTRC;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0)
> +               return irq;
> +
> +       vou->axi_clk = devm_clk_get(dev, "aclk");
> +       if (IS_ERR(vou->axi_clk))
> +               return PTR_ERR(vou->axi_clk);
> +
> +       vou->ppu_clk = devm_clk_get(dev, "ppu_wclk");
> +       if (IS_ERR(vou->ppu_clk))
> +               return PTR_ERR(vou->ppu_clk);
> +
> +       clk_prepare_enable(vou->axi_clk);
> +       clk_prepare_enable(vou->ppu_clk);
> +
> +       vou->dev = dev;
> +       priv->vou = vou;
> +       dev_set_drvdata(dev, vou);
> +
> +       vou_hw_init(vou);
> +
> +       ret = devm_request_irq(dev, irq, vou_irq_handler, 0, "zx_vou", vou);
> +       if (ret < 0)
> +               return ret;
> +
> +       vou->main_crtc = zx_crtc_init(drm, VOU_CHN_MAIN);
> +       if (IS_ERR(vou->main_crtc))
> +               return PTR_ERR(vou->main_crtc);
> +
> +       vou->aux_crtc = zx_crtc_init(drm, VOU_CHN_AUX);
> +       if (IS_ERR(vou->aux_crtc))
> +               return PTR_ERR(vou->aux_crtc);
> +
> +       return 0;
> +}
> +
> +static void zx_crtc_unbind(struct device *dev, struct device *master,
> +                          void *data)
> +{
> +       struct zx_vou *vou = dev_get_drvdata(dev);
> +
> +       clk_disable_unprepare(vou->axi_clk);
> +       clk_disable_unprepare(vou->ppu_clk);
> +}
> +
> +static const struct component_ops zx_crtc_component_ops = {
> +       .bind = zx_crtc_bind,
> +       .unbind = zx_crtc_unbind,
> +};
> +
> +static int zx_crtc_probe(struct platform_device *pdev)
> +{
> +       return component_add(&pdev->dev, &zx_crtc_component_ops);
> +}
> +
> +static int zx_crtc_remove(struct platform_device *pdev)
> +{
> +       component_del(&pdev->dev, &zx_crtc_component_ops);
> +       return 0;
> +}
> +
> +static const struct of_device_id zx_crtc_of_match[] = {
> +       { .compatible = "zte,zx296718-vou", },
> +       { /* end */ },
> +};
> +MODULE_DEVICE_TABLE(of, zx_crtc_of_match);
> +
> +struct platform_driver zx_crtc_driver = {
> +       .probe = zx_crtc_probe,
> +       .remove = zx_crtc_remove,
> +       .driver = {
> +               .name = "zx-crtc",
> +               .of_match_table = zx_crtc_of_match,
> +       },
> +};
> diff --git a/drivers/gpu/drm/zte/zx_crtc.h b/drivers/gpu/drm/zte/zx_crtc.h
> new file mode 100644
> index 000000000000..f889208054ce
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_crtc.h
> @@ -0,0 +1,47 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#ifndef __ZX_CRTC_H__
> +#define __ZX_CRTC_H__
> +
> +#define VOU_CRTC_MASK          0x3
> +
> +/* VOU output interfaces */
> +enum vou_inf_id {
> +       VOU_HDMI        = 0,
> +       VOU_RGB_LCD     = 1,
> +       VOU_TV_ENC      = 2,
> +       VOU_MIPI_DSI    = 3,
> +       VOU_LVDS        = 4,
> +       VOU_VGA         = 5,
> +};
> +
> +enum vou_inf_data_sel {
> +       VOU_YUV444      = 0,
> +       VOU_RGB_101010  = 1,
> +       VOU_RGB_888     = 2,
> +       VOU_RGB_666     = 3,
> +};
> +
> +struct vou_inf {
> +       struct drm_encoder *encoder;
> +       enum vou_inf_id id;
> +       enum vou_inf_data_sel data_sel;
> +       u32 clocks_en_bits;
> +       u32 clocks_sel_bits;
> +};
> +
> +void vou_inf_enable(struct vou_inf *inf);
> +void vou_inf_disable(struct vou_inf *inf);
> +
> +int zx_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe);
> +void zx_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe);
> +
> +#endif /* __ZX_CRTC_H__ */
> diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c
> new file mode 100644
> index 000000000000..a45ecaa7007b
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_drm_drv.c
> @@ -0,0 +1,258 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/spinlock.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/list.h>
> +#include <linux/of_graph.h>
> +#include <linux/of_platform.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_of.h>
> +
> +#include "zx_drm_drv.h"
> +#include "zx_crtc.h"
> +
> +static void zx_drm_fb_output_poll_changed(struct drm_device *drm)
> +{
> +       struct zx_drm_private *priv = drm->dev_private;
> +
> +       drm_fbdev_cma_hotplug_event(priv->fbdev);
> +}
> +
> +static const struct drm_mode_config_funcs zx_drm_mode_config_funcs = {
> +       .fb_create = drm_fb_cma_create,
> +       .output_poll_changed = zx_drm_fb_output_poll_changed,
> +       .atomic_check = drm_atomic_helper_check,
> +       .atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +static void zx_drm_lastclose(struct drm_device *drm)
> +{
> +       struct zx_drm_private *priv = drm->dev_private;
> +
> +       drm_fbdev_cma_restore_mode(priv->fbdev);
> +}
> +
> +static const struct file_operations zx_drm_fops = {
> +       .owner = THIS_MODULE,
> +       .open = drm_open,
> +       .release = drm_release,
> +       .unlocked_ioctl = drm_ioctl,
> +#ifdef CONFIG_COMPAT
> +       .compat_ioctl = drm_compat_ioctl,
> +#endif
> +       .poll = drm_poll,
> +       .read = drm_read,
> +       .llseek = noop_llseek,
> +       .mmap = drm_gem_cma_mmap,
> +};
> +
> +static struct drm_driver zx_drm_driver = {
> +       .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
> +                          DRIVER_ATOMIC,
> +       .lastclose = zx_drm_lastclose,
> +       .get_vblank_counter = drm_vblank_no_hw_counter,
> +       .enable_vblank = zx_crtc_enable_vblank,
> +       .disable_vblank = zx_crtc_disable_vblank,
> +       .gem_free_object = drm_gem_cma_free_object,
> +       .gem_vm_ops = &drm_gem_cma_vm_ops,
> +       .dumb_create = drm_gem_cma_dumb_create,
> +       .dumb_map_offset = drm_gem_cma_dumb_map_offset,
> +       .dumb_destroy = drm_gem_dumb_destroy,
> +       .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> +       .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> +       .gem_prime_export = drm_gem_prime_export,
> +       .gem_prime_import = drm_gem_prime_import,
> +       .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,
> +       .fops = &zx_drm_fops,
> +       .name = "zx-vou",
> +       .desc = "ZTE VOU Controller DRM",
> +       .date = "20160811",
> +       .major = 1,
> +       .minor = 0,
> +};
> +
> +static int zx_drm_bind(struct device *dev)
> +{
> +       struct drm_device *drm;
> +       struct zx_drm_private *priv;
> +       int ret;
> +
> +       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +       if (!priv)
> +               return -ENOMEM;
> +
> +       drm = drm_dev_alloc(&zx_drm_driver, dev);
> +       if (!drm)
> +               return -ENOMEM;
> +
> +       drm->dev_private = priv;
> +       dev_set_drvdata(dev, drm);
> +
> +       drm_mode_config_init(drm);
> +       drm->mode_config.min_width = 16;
> +       drm->mode_config.min_height = 16;
> +       drm->mode_config.max_width = 4096;
> +       drm->mode_config.max_height = 4096;
> +       drm->mode_config.funcs = &zx_drm_mode_config_funcs;
> +
> +       ret = drm_dev_register(drm, 0);
> +       if (ret)
> +               goto out_free;
> +
> +       ret = component_bind_all(dev, drm);
> +       if (ret) {
> +               DRM_ERROR("Failed to bind all components\n");
> +               goto out_unregister;
> +       }
> +
> +       ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
> +       if (ret < 0) {
> +               DRM_ERROR("failed to initialise vblank\n");
> +               goto out_unbind;
> +       }
> +
> +       /*
> +        * We will manage irq handler on our own.  In this case, irq_enabled
> +        * need to be true for using vblank core support.
> +        */
> +       drm->irq_enabled = true;
> +
> +       drm_mode_config_reset(drm);
> +       drm_kms_helper_poll_init(drm);
> +
> +       priv->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc,
> +                                        drm->mode_config.num_connector);
> +       if (IS_ERR(priv->fbdev)) {
> +               ret = PTR_ERR(priv->fbdev);
> +               priv->fbdev = NULL;
> +               goto out_fini;
> +       }
> +
> +       return 0;
> +
> +out_fini:
> +       drm_kms_helper_poll_fini(drm);
> +       drm_mode_config_cleanup(drm);
> +       drm_vblank_cleanup(drm);
> +out_unbind:
> +       component_unbind_all(dev, drm);
> +out_unregister:
> +       drm_dev_unregister(drm);
> +out_free:
> +       dev_set_drvdata(dev, NULL);
> +       drm_dev_unref(drm);
> +       return ret;
> +}
> +
> +static void zx_drm_unbind(struct device *dev)
> +{
> +       struct drm_device *drm = dev_get_drvdata(dev);
> +       struct zx_drm_private *priv = drm->dev_private;
> +
> +       if (priv->fbdev) {
> +               drm_fbdev_cma_fini(priv->fbdev);
> +               priv->fbdev = NULL;
> +       }
> +       drm_kms_helper_poll_fini(drm);
> +       component_unbind_all(dev, drm);
> +       drm_vblank_cleanup(drm);
> +       drm_mode_config_cleanup(drm);
> +       drm_dev_unregister(drm);
> +       drm_dev_unref(drm);
> +       drm->dev_private = NULL;
> +       dev_set_drvdata(dev, NULL);
> +}
> +
> +static const struct component_master_ops zx_drm_master_ops = {
> +       .bind = zx_drm_bind,
> +       .unbind = zx_drm_unbind,
> +};
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> +       return dev->of_node == data;
> +}
> +
> +static int zx_drm_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct device_node *parent = dev->of_node;
> +       struct device_node *child;
> +       struct component_match *match = NULL;
> +       int ret;
> +
> +       ret = of_platform_populate(parent, NULL, NULL, dev);
> +       if (ret)
> +               return ret;
> +
> +       for_each_available_child_of_node(parent, child) {
> +               component_match_add(dev, &match, compare_of, child);
> +               of_node_put(child);
> +       }
> +
> +       return component_master_add_with_match(dev, &zx_drm_master_ops, match);
> +}
> +
> +static int zx_drm_remove(struct platform_device *pdev)
> +{
> +       component_master_del(&pdev->dev, &zx_drm_master_ops);
> +       return 0;
> +}
> +
> +static const struct of_device_id zx_drm_of_match[] = {
> +       { .compatible = "zte,zx-display-subsystem", },
> +       { /* end */ },
> +};
> +MODULE_DEVICE_TABLE(of, zx_drm_of_match);
> +
> +static struct platform_driver zx_drm_platform_driver = {
> +       .probe = zx_drm_probe,
> +       .remove = zx_drm_remove,
> +       .driver = {
> +               .name = "zx-drm",
> +               .of_match_table = zx_drm_of_match,
> +       },
> +};
> +
> +static struct platform_driver *drivers[] = {
> +       &zx_crtc_driver,
> +       &zx_hdmi_driver,
> +       &zx_drm_platform_driver,
> +};
> +
> +static int zx_drm_init(void)
> +{
> +       return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
> +}
> +module_init(zx_drm_init);
> +
> +static void zx_drm_exit(void)
> +{
> +       platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
> +}
> +module_exit(zx_drm_exit);
> +
> +MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
> +MODULE_DESCRIPTION("ZTE ZX VOU DRM driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/zte/zx_drm_drv.h b/drivers/gpu/drm/zte/zx_drm_drv.h
> new file mode 100644
> index 000000000000..6aac303de6aa
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_drm_drv.h
> @@ -0,0 +1,22 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#ifndef __ZX_DRM_DRV_H__
> +#define __ZX_DRM_DRV_H__
> +
> +struct zx_drm_private {
> +       struct drm_fbdev_cma *fbdev;
> +       struct zx_vou *vou;
> +};
> +
> +extern struct platform_driver zx_crtc_driver;
> +extern struct platform_driver zx_hdmi_driver;
> +
> +#endif /* __ZX_DRM_DRV_H__ */
> diff --git a/drivers/gpu/drm/zte/zx_hdmi.c b/drivers/gpu/drm/zte/zx_hdmi.c
> new file mode 100644
> index 000000000000..5aaab8493b1b
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_hdmi.c
> @@ -0,0 +1,540 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#include <drm/drm_of.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_edid.h>
> +#include <linux/irq.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/hdmi.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_device.h>
> +#include <linux/component.h>
> +
> +#include "zx_crtc.h"
> +
> +#define FUNC_SEL                       0x000b
> +#define FUNC_HDMI_EN                   BIT(0)
> +#define CLKPWD                         0x000d
> +#define CLKPWD_PDIDCK                  BIT(2)
> +#define PWD_SRST                       0x0010
> +#define P2T_CTRL                       0x0066
> +#define P2T_DC_PKT_EN                  BIT(7)
> +#define L1_INTR_STAT                   0x007e
> +#define L1_INTR_STAT_INTR1             BIT(0)
> +#define INTR1_STAT                     0x008f
> +#define INTR1_MASK                     0x0095
> +#define INTR1_MONITOR_DETECT           (BIT(5) | BIT(6))
> +#define ZX_DDC_ADDR                    0x00ed
> +#define ZX_DDC_SEGM                    0x00ee
> +#define ZX_DDC_OFFSET                  0x00ef
> +#define ZX_DDC_DIN_CNT1                        0x00f0
> +#define ZX_DDC_DIN_CNT2                        0x00f1
> +#define ZX_DDC_CMD                     0x00f3
> +#define DDC_CMD_MASK                   0xf
> +#define DDC_CMD_CLEAR_FIFO             0x9
> +#define DDC_CMD_SEQUENTIAL_READ                0x2
> +#define ZX_DDC_DATA                    0x00f4
> +#define ZX_DDC_DOUT_CNT                        0x00f5
> +#define DDC_DOUT_CNT_MASK              0x1f
> +#define TEST_TXCTRL                    0x00f7
> +#define TEST_TXCTRL_HDMI_MODE          BIT(1)
> +#define HDMICTL4                       0x0235
> +#define TPI_HPD_RSEN                   0x063b
> +#define TPI_HPD_CONNECTION             (BIT(1) | BIT(2))
> +#define TPI_INFO_FSEL                  0x06bf
> +#define FSEL_AVI                       0
> +#define FSEL_GBD                       1
> +#define FSEL_AUDIO                     2
> +#define FSEL_SPD                       3
> +#define FSEL_MPEG                      4
> +#define FSEL_VSIF                      5
> +#define TPI_INFO_B0                    0x06c0
> +#define TPI_INFO_EN                    0x06df
> +#define TPI_INFO_TRANS_EN              BIT(7)
> +#define TPI_INFO_TRANS_RPT             BIT(6)
> +#define TPI_DDC_MASTER_EN              0x06f8
> +#define HW_DDC_MASTER                  BIT(7)
> +
> +#define ZX_HDMI_INFOFRAME_SIZE         31
> +
> +struct zx_hdmi {
> +       struct drm_connector connector;
> +       struct drm_encoder encoder;
> +       struct device *dev;
> +       struct drm_device *drm;
> +       void __iomem *mmio;
> +       struct clk *cec_clk;
> +       struct clk *osc_clk;
> +       struct clk *xclk;
> +       bool sink_is_hdmi;
> +       bool sink_has_audio;
> +       struct vou_inf *inf;
> +};
> +
> +#define to_zx_hdmi(x)  container_of(x, struct zx_hdmi, x)
> +
> +static struct vou_inf vou_inf_hdmi = {
> +       .id = VOU_HDMI,
> +       .data_sel = VOU_YUV444,
> +       .clocks_en_bits = BIT(24) | BIT(18) | BIT(6),
> +       .clocks_sel_bits = BIT(13) | BIT(2),
> +};
> +
> +static inline u8 hdmi_readb(struct zx_hdmi *hdmi, u16 offset)
> +{
> +       return readl_relaxed(hdmi->mmio + offset * 4);
> +}
> +
> +static inline void hdmi_writeb(struct zx_hdmi *hdmi, u16 offset, u8 val)
> +{
> +       writel_relaxed(val, hdmi->mmio + offset * 4);
> +}
> +
> +static int zx_hdmi_infoframe_trans(struct zx_hdmi *hdmi,
> +                                  union hdmi_infoframe *frame, u8 fsel)
> +{
> +       u8 buffer[ZX_HDMI_INFOFRAME_SIZE];
> +       u8 val;
> +       ssize_t num;
> +       int i;
> +
> +       hdmi_writeb(hdmi, TPI_INFO_FSEL, fsel);
> +
> +       num = hdmi_infoframe_pack(frame, buffer, ZX_HDMI_INFOFRAME_SIZE);
> +       if (num < 0)
> +               return num;
> +
> +       for (i = 0; i < num; i++)
> +               hdmi_writeb(hdmi, TPI_INFO_B0 + i, buffer[i]);
> +
> +       val = hdmi_readb(hdmi, TPI_INFO_EN);
> +       val |= TPI_INFO_TRANS_EN | TPI_INFO_TRANS_RPT;
> +       hdmi_writeb(hdmi, TPI_INFO_EN, val);
> +
> +       return num;
> +}
> +
> +static int zx_hdmi_config_video_vsi(struct zx_hdmi *hdmi,
> +                                   struct drm_display_mode *mode)
> +{
> +       union hdmi_infoframe frame;
> +       int ret;
> +
> +       ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
> +                                                         mode);
> +       if (ret)
> +               return ret;
> +
> +       return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_VSIF);
> +}
> +
> +static int zx_hdmi_config_video_avi(struct zx_hdmi *hdmi,
> +                                   struct drm_display_mode *mode)
> +{
> +       union hdmi_infoframe frame;
> +       int ret;
> +
> +       ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode);
> +       if (ret)
> +               return ret;
> +
> +       /* We always use YUV444 for HDMI output. */
> +       frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
> +
> +       return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_AVI);
> +}
> +
> +static void zx_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> +                                    struct drm_display_mode *mode,
> +                                    struct drm_display_mode *adj_mode)
> +{
> +       struct zx_hdmi *hdmi = to_zx_hdmi(encoder);
> +
> +       if (hdmi->sink_is_hdmi) {
> +               zx_hdmi_config_video_avi(hdmi, mode);
> +               zx_hdmi_config_video_vsi(hdmi, mode);
> +       }
> +}
> +
> +static void zx_hdmi_encoder_enable(struct drm_encoder *encoder)
> +{
> +       struct zx_hdmi *hdmi = to_zx_hdmi(encoder);
> +
> +       vou_inf_enable(hdmi->inf);
> +}
> +
> +static void zx_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +       struct zx_hdmi *hdmi = to_zx_hdmi(encoder);
> +
> +       vou_inf_disable(hdmi->inf);
> +}
> +
> +static const struct drm_encoder_helper_funcs zx_hdmi_encoder_helper_funcs = {
> +       .enable = zx_hdmi_encoder_enable,
> +       .disable = zx_hdmi_encoder_disable,
> +       .mode_set = zx_hdmi_encoder_mode_set,
> +};
> +
> +static const struct drm_encoder_funcs zx_hdmi_encoder_funcs = {
> +       .destroy = drm_encoder_cleanup,
> +};
> +
> +static int zx_hdmi_get_edid_block(void *data, u8 *buf, unsigned int block,
> +                                 size_t len)
> +{
> +       struct zx_hdmi *hdmi = data;
> +       int retry = 0;
> +       int ret = 0;
> +       int i = 0;
> +       u8 val;
> +
> +       /* Enable DDC master access */
> +       val = hdmi_readb(hdmi, TPI_DDC_MASTER_EN);
> +       val |= HW_DDC_MASTER;
> +       hdmi_writeb(hdmi, TPI_DDC_MASTER_EN, val);
> +
> +       hdmi_writeb(hdmi, ZX_DDC_ADDR, 0xa0);
> +       hdmi_writeb(hdmi, ZX_DDC_OFFSET, block * EDID_LENGTH);
> +       /* Bits [9:8] of bytes */
> +       hdmi_writeb(hdmi, ZX_DDC_DIN_CNT2, (len >> 8) & 0xff);
> +       /* Bits [7:0] of bytes */
> +       hdmi_writeb(hdmi, ZX_DDC_DIN_CNT1, len & 0xff);
> +
> +       /* Clear FIFO */
> +       val = hdmi_readb(hdmi, ZX_DDC_CMD);
> +       val &= ~DDC_CMD_MASK;
> +       val |= DDC_CMD_CLEAR_FIFO;
> +       hdmi_writeb(hdmi, ZX_DDC_CMD, val);
> +
> +       /* Kick off the read */
> +       val = hdmi_readb(hdmi, ZX_DDC_CMD);
> +       val &= ~DDC_CMD_MASK;
> +       val |= DDC_CMD_SEQUENTIAL_READ;
> +       hdmi_writeb(hdmi, ZX_DDC_CMD, val);
> +
> +       while (len > 0) {
> +               int cnt, j;
> +
> +               /* FIFO needs some time to get ready */
> +               usleep_range(500, 1000);
> +
> +               cnt = hdmi_readb(hdmi, ZX_DDC_DOUT_CNT) & DDC_DOUT_CNT_MASK;
> +               if (cnt == 0) {
> +                       if (++retry > 5) {
> +                               dev_err(hdmi->dev, "DDC read timed out!");
> +                               ret = -ETIMEDOUT;
> +                               break;
> +                       }
> +                       continue;
> +               }
> +
> +               for (j = 0; j < cnt; j++)
> +                       buf[i++] = hdmi_readb(hdmi, ZX_DDC_DATA);
> +               len -= cnt;
> +       }
> +
> +       /* Disable DDC master access */
> +       val = hdmi_readb(hdmi, TPI_DDC_MASTER_EN);
> +       val &= ~HW_DDC_MASTER;
> +       hdmi_writeb(hdmi, TPI_DDC_MASTER_EN, val);
> +
> +       return ret;
> +}
> +
> +static int zx_hdmi_connector_get_modes(struct drm_connector *connector)
> +{
> +       struct zx_hdmi *hdmi = to_zx_hdmi(connector);
> +       struct edid *edid;
> +       int ret = 0;
> +
> +       edid = drm_do_get_edid(connector, zx_hdmi_get_edid_block, hdmi);
> +       if (edid) {
> +               hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> +               hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
> +               drm_mode_connector_update_edid_property(connector, edid);
> +               ret = drm_add_edid_modes(connector, edid);
> +               kfree(edid);
> +       }
> +
> +       return ret;
> +}
> +
> +static enum drm_mode_status
> +zx_hdmi_connector_mode_valid(struct drm_connector *connector,
> +                            struct drm_display_mode *mode)
> +{
> +       return MODE_OK;
> +}
> +
> +static struct drm_connector_helper_funcs zx_hdmi_connector_helper_funcs = {
> +       .get_modes = zx_hdmi_connector_get_modes,
> +       .mode_valid = zx_hdmi_connector_mode_valid,
> +};
> +
> +static enum drm_connector_status
> +zx_hdmi_connector_detect(struct drm_connector *connector, bool force)
> +{
> +       struct zx_hdmi *hdmi = to_zx_hdmi(connector);
> +
> +       return (hdmi_readb(hdmi, TPI_HPD_RSEN) & TPI_HPD_CONNECTION) ?
> +               connector_status_connected : connector_status_disconnected;
> +}
> +
> +static void zx_hdmi_connector_destroy(struct drm_connector *connector)
> +{
> +       drm_connector_unregister(connector);
> +       drm_connector_cleanup(connector);
> +}
> +
> +static const struct drm_connector_funcs zx_hdmi_connector_funcs = {
> +       .dpms = drm_atomic_helper_connector_dpms,
> +       .fill_modes = drm_helper_probe_single_connector_modes,
> +       .detect = zx_hdmi_connector_detect,
> +       .destroy = zx_hdmi_connector_destroy,
> +       .reset = drm_atomic_helper_connector_reset,
> +       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int zx_hdmi_register(struct drm_device *drm, struct zx_hdmi *hdmi)
> +{
> +       struct drm_encoder *encoder = &hdmi->encoder;
> +
> +       encoder->possible_crtcs = VOU_CRTC_MASK;
> +
> +       drm_encoder_init(drm, encoder, &zx_hdmi_encoder_funcs,
> +                        DRM_MODE_ENCODER_TMDS, NULL);
> +       drm_encoder_helper_add(encoder, &zx_hdmi_encoder_helper_funcs);
> +
> +       hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> +
> +       drm_connector_init(drm, &hdmi->connector, &zx_hdmi_connector_funcs,
> +                          DRM_MODE_CONNECTOR_HDMIA);
> +       drm_connector_helper_add(&hdmi->connector,
> +                                &zx_hdmi_connector_helper_funcs);
> +
> +       drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
> +       drm_connector_register(&hdmi->connector);
> +
> +       return 0;
> +}
> +
> +static irqreturn_t zx_hdmi_irq_thread(int irq, void *dev_id)
> +{
> +       struct zx_hdmi *hdmi = dev_id;
> +
> +       drm_helper_hpd_irq_event(hdmi->connector.dev);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t zx_hdmi_irq_handler(int irq, void *dev_id)
> +{
> +       struct zx_hdmi *hdmi = dev_id;
> +       u8 lstat;
> +
> +       lstat = hdmi_readb(hdmi, L1_INTR_STAT);
> +
> +       /* Monitor detect/HPD interrupt */
> +       if (lstat & L1_INTR_STAT_INTR1) {
> +               u8 stat = hdmi_readb(hdmi, INTR1_STAT);
> +
> +               hdmi_writeb(hdmi, INTR1_STAT, stat);
> +               if (stat & INTR1_MONITOR_DETECT)
> +                       return IRQ_WAKE_THREAD;
> +       }
> +
> +       return IRQ_NONE;
> +}
> +
> +static void zx_hdmi_phy_start(struct zx_hdmi *hdmi)
> +{
> +       /* Copy from ZTE BSP code */
> +       hdmi_writeb(hdmi, 0x222, 0x0);
> +       hdmi_writeb(hdmi, 0x224, 0x4);
> +       hdmi_writeb(hdmi, 0x909, 0x0);
> +       hdmi_writeb(hdmi, 0x7b0, 0x90);
> +       hdmi_writeb(hdmi, 0x7b1, 0x00);
> +       hdmi_writeb(hdmi, 0x7b2, 0xa7);
> +       hdmi_writeb(hdmi, 0x7b8, 0xaa);
> +       hdmi_writeb(hdmi, 0x7b2, 0xa7);
> +       hdmi_writeb(hdmi, 0x7b3, 0x0f);
> +       hdmi_writeb(hdmi, 0x7b4, 0x0f);
> +       hdmi_writeb(hdmi, 0x7b5, 0x55);
> +       hdmi_writeb(hdmi, 0x7b7, 0x03);
> +       hdmi_writeb(hdmi, 0x7b9, 0x12);
> +       hdmi_writeb(hdmi, 0x7ba, 0x32);
> +       hdmi_writeb(hdmi, 0x7bc, 0x68);
> +       hdmi_writeb(hdmi, 0x7be, 0x40);
> +       hdmi_writeb(hdmi, 0x7bf, 0x84);
> +       hdmi_writeb(hdmi, 0x7c1, 0x0f);
> +       hdmi_writeb(hdmi, 0x7c8, 0x02);
> +       hdmi_writeb(hdmi, 0x7c9, 0x03);
> +       hdmi_writeb(hdmi, 0x7ca, 0x40);
> +       hdmi_writeb(hdmi, 0x7dc, 0x31);
> +       hdmi_writeb(hdmi, 0x7e2, 0x04);
> +       hdmi_writeb(hdmi, 0x7e0, 0x06);
> +       hdmi_writeb(hdmi, 0x7cb, 0x68);
> +       hdmi_writeb(hdmi, 0x7f9, 0x02);
> +       hdmi_writeb(hdmi, 0x7b6, 0x02);
> +       hdmi_writeb(hdmi, 0x7f3, 0x0);
> +}
> +
> +static void zx_hdmi_hw_init(struct zx_hdmi *hdmi)
> +{
> +       u8 val;
> +
> +       /* Software reset */
> +       hdmi_writeb(hdmi, PWD_SRST, 1);
> +
> +       /* Enable pclk */
> +       val = hdmi_readb(hdmi, CLKPWD);
> +       val |= CLKPWD_PDIDCK;
> +       hdmi_writeb(hdmi, CLKPWD, val);
> +
> +       /* Enable HDMI for TX */
> +       val = hdmi_readb(hdmi, FUNC_SEL);
> +       val |= FUNC_HDMI_EN;
> +       hdmi_writeb(hdmi, FUNC_SEL, val);
> +
> +       /* Enable deep color packet */
> +       val = hdmi_readb(hdmi, P2T_CTRL);
> +       val |= P2T_DC_PKT_EN;
> +       hdmi_writeb(hdmi, P2T_CTRL, val);
> +
> +       /* Enable HDMI/MHL mode for output */
> +       val = hdmi_readb(hdmi, TEST_TXCTRL);
> +       val |= TEST_TXCTRL_HDMI_MODE;
> +       hdmi_writeb(hdmi, TEST_TXCTRL, val);
> +
> +       /* Configure reg_qc_sel */
> +       hdmi_writeb(hdmi, HDMICTL4, 0x3);
> +
> +       /* Enable interrupt */
> +       val = hdmi_readb(hdmi, INTR1_MASK);
> +       val |= INTR1_MONITOR_DETECT;
> +       hdmi_writeb(hdmi, INTR1_MASK, val);
> +
> +       /* Clear reset for normal operation */
> +       hdmi_writeb(hdmi, PWD_SRST, 0);
> +
> +       /* Start up phy */
> +       zx_hdmi_phy_start(hdmi);
> +}
> +
> +static int zx_hdmi_bind(struct device *dev, struct device *master, void *data)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct drm_device *drm = data;
> +       struct resource *res;
> +       struct zx_hdmi *hdmi;
> +       struct vou_inf *inf;
> +       int irq;
> +       int ret;
> +
> +       hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
> +       if (!hdmi)
> +               return -ENOMEM;
> +
> +       hdmi->dev = dev;
> +       hdmi->drm = drm;
> +
> +       inf = &vou_inf_hdmi;
> +       inf->encoder = &hdmi->encoder;
> +       hdmi->inf = inf;
> +
> +       dev_set_drvdata(dev, hdmi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       hdmi->mmio = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(hdmi->mmio))
> +               return PTR_ERR(hdmi->mmio);
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0)
> +               return irq;
> +
> +       hdmi->cec_clk = devm_clk_get(hdmi->dev, "osc_cec");
> +       if (IS_ERR(hdmi->cec_clk))
> +               return PTR_ERR(hdmi->cec_clk);
> +
> +       hdmi->osc_clk = devm_clk_get(hdmi->dev, "osc_clk");
> +       if (IS_ERR(hdmi->osc_clk))
> +               return PTR_ERR(hdmi->osc_clk);
> +
> +       hdmi->xclk = devm_clk_get(hdmi->dev, "xclk");
> +       if (IS_ERR(hdmi->xclk))
> +               return PTR_ERR(hdmi->xclk);
> +
> +       zx_hdmi_hw_init(hdmi);
> +
> +       clk_prepare_enable(hdmi->cec_clk);
> +       clk_prepare_enable(hdmi->osc_clk);
> +       clk_prepare_enable(hdmi->xclk);
> +
> +       ret = zx_hdmi_register(drm, hdmi);
> +       if (ret)
> +               return ret;
> +
> +       ret = devm_request_threaded_irq(dev, irq, zx_hdmi_irq_handler,
> +                                       zx_hdmi_irq_thread, IRQF_SHARED,
> +                                       dev_name(dev), hdmi);
> +
> +       return 0;
> +}
> +
> +static void zx_hdmi_unbind(struct device *dev, struct device *master,
> +                          void *data)
> +{
> +       struct zx_hdmi *hdmi = dev_get_drvdata(dev);
> +
> +       clk_disable_unprepare(hdmi->cec_clk);
> +       clk_disable_unprepare(hdmi->osc_clk);
> +       clk_disable_unprepare(hdmi->xclk);
> +}
> +
> +static const struct component_ops zx_hdmi_component_ops = {
> +       .bind = zx_hdmi_bind,
> +       .unbind = zx_hdmi_unbind,
> +};
> +
> +static int zx_hdmi_probe(struct platform_device *pdev)
> +{
> +       return component_add(&pdev->dev, &zx_hdmi_component_ops);
> +}
> +
> +static int zx_hdmi_remove(struct platform_device *pdev)
> +{
> +       component_del(&pdev->dev, &zx_hdmi_component_ops);
> +       return 0;
> +}
> +
> +static const struct of_device_id zx_hdmi_of_match[] = {
> +       { .compatible = "zte,zx296718-hdmi", },
> +       { /* end */ },
> +};
> +MODULE_DEVICE_TABLE(of, zx_hdmi_of_match);
> +
> +struct platform_driver zx_hdmi_driver = {
> +       .probe = zx_hdmi_probe,
> +       .remove = zx_hdmi_remove,
> +       .driver = {
> +               .name = "zx-hdmi",
> +               .of_match_table = zx_hdmi_of_match,
> +       },
> +};
> diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c
> new file mode 100644
> index 000000000000..7d1f3bbed210
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_plane.c
> @@ -0,0 +1,362 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_modeset_helper_vtables.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "zx_crtc.h"
> +#include "zx_plane.h"
> +
> +/* GL registers */
> +#define GL_CTRL0                       0x00
> +#define GL_UPDATE                      BIT(5)
> +#define GL_CTRL1                       0x04
> +#define GL_DATA_FMT_SHIFT              0
> +#define GL_DATA_FMT_MASK               (0xf << GL_DATA_FMT_SHIFT)
> +#define GL_FMT_ARGB8888                        0
> +#define GL_FMT_RGB888                  1
> +#define GL_FMT_RGB565                  2
> +#define GL_FMT_ARGB1555                        3
> +#define GL_FMT_ARGB4444                        4
> +#define GL_CTRL2                       0x08
> +#define GL_GLOBAL_ALPHA_SHIFT          8
> +#define GL_GLOBAL_ALPHA_MASK           (0xff << GL_GLOBAL_ALPHA_SHIFT)
> +#define GL_CTRL3                       0x0c
> +#define GL_SCALER_BYPASS_MODE          BIT(0)
> +#define GL_STRIDE                      0x18
> +#define GL_ADDR                                0x1c
> +#define GL_SRC_SIZE                    0x38
> +#define GL_SRC_W_SHIFT                 16
> +#define GL_SRC_W_MASK                  (0x3fff << GL_SRC_W_SHIFT)
> +#define GL_SRC_H_SHIFT                 0
> +#define GL_SRC_H_MASK                  (0x3fff << GL_SRC_H_SHIFT)
> +#define GL_POS_START                   0x9c
> +#define GL_POS_END                     0xa0
> +#define GL_POS_X_SHIFT                 16
> +#define GL_POS_X_MASK                  (0x1fff << GL_POS_X_SHIFT)
> +#define GL_POS_Y_SHIFT                 0
> +#define GL_POS_Y_MASK                  (0x1fff << GL_POS_Y_SHIFT)
> +
> +/* CSC registers */
> +#define CSC_CTRL0                      0x30
> +#define CSC_COV_MODE_SHIFT             16
> +#define CSC_COV_MODE_MASK              (0xffff << CSC_COV_MODE_SHIFT)
> +#define CSC_BT601_IMAGE_RGB2YCBCR      0
> +#define CSC_BT601_IMAGE_YCBCR2RGB      1
> +#define CSC_BT601_VIDEO_RGB2YCBCR      2
> +#define CSC_BT601_VIDEO_YCBCR2RGB      3
> +#define CSC_BT709_IMAGE_RGB2YCBCR      4
> +#define CSC_BT709_IMAGE_YCBCR2RGB      5
> +#define CSC_BT709_VIDEO_RGB2YCBCR      6
> +#define CSC_BT709_VIDEO_YCBCR2RGB      7
> +#define CSC_BT2020_IMAGE_RGB2YCBCR     8
> +#define CSC_BT2020_IMAGE_YCBCR2RGB     9
> +#define CSC_BT2020_VIDEO_RGB2YCBCR     10
> +#define CSC_BT2020_VIDEO_YCBCR2RGB     11
> +#define CSC_WORK_ENABLE                        BIT(0)
> +
> +/* RSZ registers */
> +#define RSZ_SRC_CFG                    0x00
> +#define RSZ_DEST_CFG                   0x04
> +#define RSZ_ENABLE_CFG                 0x14
> +
> +/* HBSC registers */
> +#define HBSC_SATURATION                        0x00
> +#define HBSC_HUE                       0x04
> +#define HBSC_BRIGHT                    0x08
> +#define HBSC_CONTRAST                  0x0c
> +#define HBSC_THRESHOLD_COL1            0x10
> +#define HBSC_THRESHOLD_COL2            0x14
> +#define HBSC_THRESHOLD_COL3            0x18
> +#define HBSC_CTRL0                     0x28
> +#define HBSC_CTRL_EN                   BIT(2)
> +
> +struct zx_plane {
> +       struct drm_plane plane;
> +       void __iomem *layer;
> +       void __iomem *csc;
> +       void __iomem *hbsc;
> +       void __iomem *rsz;
> +};
> +
> +#define to_zx_plane(plane)     container_of(plane, struct zx_plane, plane)
> +
> +static const uint32_t gl_formats[] = {
> +       DRM_FORMAT_ARGB8888,
> +       DRM_FORMAT_XRGB8888,
> +       DRM_FORMAT_RGB888,
> +       DRM_FORMAT_RGB565,
> +       DRM_FORMAT_ARGB1555,
> +       DRM_FORMAT_ARGB4444,
> +};
> +
> +static int zx_gl_plane_atomic_check(struct drm_plane *plane,
> +                                   struct drm_plane_state *state)
> +{
> +       u32 src_w, src_h;
> +
> +       src_w = state->src_w >> 16;
> +       src_h = state->src_h >> 16;
> +
> +       /* TODO: support scaling of the plane source */
> +       if ((src_w != state->crtc_w) || (src_h != state->crtc_h))
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static int zx_gl_get_fmt(uint32_t format)
> +{
> +       switch (format) {
> +       case DRM_FORMAT_ARGB8888:
> +       case DRM_FORMAT_XRGB8888:
> +               return GL_FMT_ARGB8888;
> +       case DRM_FORMAT_RGB888:
> +               return GL_FMT_RGB888;
> +       case DRM_FORMAT_RGB565:
> +               return GL_FMT_RGB565;
> +       case DRM_FORMAT_ARGB1555:
> +               return GL_FMT_ARGB1555;
> +       case DRM_FORMAT_ARGB4444:
> +               return GL_FMT_ARGB4444;
> +       default:
> +               WARN_ONCE(1, "invalid pixel format %d\n", format);
> +               return -EINVAL;
> +       }
> +}
> +
> +static inline void zx_gl_set_update(struct zx_plane *zplane)
> +{
> +       void __iomem *layer = zplane->layer;
> +       u32 val;
> +
> +       val = readl(layer + GL_CTRL0);
> +       val |= GL_UPDATE;
> +       writel(val, layer + GL_CTRL0);
> +}
> +
> +static inline void zx_gl_rsz_set_update(struct zx_plane *zplane)
> +{
> +       writel(1, zplane->rsz + RSZ_ENABLE_CFG);
> +}
> +
> +void zx_plane_set_update(struct drm_plane *plane)
> +{
> +       struct zx_plane *zplane = to_zx_plane(plane);
> +
> +       zx_gl_rsz_set_update(zplane);
> +       zx_gl_set_update(zplane);
> +}
> +
> +static void zx_gl_rsz_setup(struct zx_plane *zplane, u32 src_w, u32 src_h,
> +                           u32 dst_w, u32 dst_h)
> +{
> +       void __iomem *rsz = zplane->rsz;
> +       u32 val;
> +
> +       val = ((src_h - 1) & 0xffff) << 16;
> +       val |= (src_w - 1) & 0xffff;
> +       writel(val, rsz + RSZ_SRC_CFG);
> +
> +       val = ((dst_h - 1) & 0xffff) << 16;
> +       val |= (dst_w - 1) & 0xffff;
> +       writel(val, rsz + RSZ_DEST_CFG);
> +
> +       zx_gl_rsz_set_update(zplane);
> +}
> +
> +static void zx_gl_plane_atomic_update(struct drm_plane *plane,
> +                                     struct drm_plane_state *old_state)
> +{
> +       struct zx_plane *zplane = to_zx_plane(plane);
> +       struct drm_framebuffer *fb = plane->state->fb;
> +       struct drm_gem_cma_object *cma_obj;
> +       void __iomem *layer = zplane->layer;
> +       void __iomem *csc = zplane->csc;
> +       void __iomem *hbsc = zplane->hbsc;
> +       u32 src_x, src_y, src_w, src_h;
> +       u32 dst_x, dst_y, dst_w, dst_h;
> +       unsigned int depth, bpp;
> +       uint32_t format;
> +       dma_addr_t paddr;
> +       u32 stride;
> +       int fmt;
> +       u32 val;
> +
> +       if (!fb)
> +               return;
> +
> +       format = fb->pixel_format;
> +       stride = fb->pitches[0];
> +
> +       src_x = plane->state->src_x >> 16;
> +       src_y = plane->state->src_y >> 16;
> +       src_w = plane->state->src_w >> 16;
> +       src_h = plane->state->src_h >> 16;
> +
> +       dst_x = plane->state->crtc_x;
> +       dst_y = plane->state->crtc_y;
> +       dst_w = plane->state->crtc_w;
> +       dst_h = plane->state->crtc_h;
> +
> +       drm_fb_get_bpp_depth(format, &depth, &bpp);
> +
> +       cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
> +       paddr = cma_obj->paddr + fb->offsets[0];
> +       paddr += src_y * stride + src_x * bpp / 8;
> +       writel(paddr, layer + GL_ADDR);
> +
> +       /* Set up source height/width register */
> +       val = (src_w << GL_SRC_W_SHIFT) & GL_SRC_W_MASK;
> +       val |= (src_h << GL_SRC_H_SHIFT) & GL_SRC_H_MASK;
> +       writel(val, layer + GL_SRC_SIZE);
> +
> +       /* Set up start position register */
> +       val = (dst_x << GL_POS_X_SHIFT) & GL_POS_X_MASK;
> +       val |= (dst_y << GL_POS_Y_SHIFT) & GL_POS_Y_MASK;
> +       writel(val, layer + GL_POS_START);
> +
> +       /* Set up end position register */
> +       val = ((dst_x + dst_w) << GL_POS_X_SHIFT) & GL_POS_X_MASK;
> +       val |= ((dst_y + dst_h) << GL_POS_Y_SHIFT) & GL_POS_Y_MASK;
> +       writel(val, layer + GL_POS_END);
> +
> +       /* Set up stride register */
> +       writel(stride & 0xffff, layer + GL_STRIDE);
> +
> +       /* Set up graphic layer data format */
> +       fmt = zx_gl_get_fmt(format);
> +       if (fmt >= 0) {
> +               val = readl(layer + GL_CTRL1);
> +               val &= ~GL_DATA_FMT_MASK;
> +               val |= fmt << GL_DATA_FMT_SHIFT;
> +               writel(val, layer + GL_CTRL1);
> +       }
> +
> +       /* Initialize global alpha with a sane value */
> +       val = readl(layer + GL_CTRL2);
> +       val &= ~GL_GLOBAL_ALPHA_MASK;
> +       val |= 0xff << GL_GLOBAL_ALPHA_SHIFT;
> +       writel(val, layer + GL_CTRL2);
> +
> +       /* Setup CSC for the GL */
> +       val = readl(csc + CSC_CTRL0);
> +       val &= ~CSC_COV_MODE_MASK;
> +       if (dst_h > 720)
> +               val |= CSC_BT709_IMAGE_RGB2YCBCR << CSC_COV_MODE_SHIFT;
> +       else
> +               val |= CSC_BT601_IMAGE_RGB2YCBCR << CSC_COV_MODE_SHIFT;
> +       val |= CSC_WORK_ENABLE;
> +       writel(val, csc + CSC_CTRL0);
> +
> +       /* Always use scaler since it exists */
> +       val = readl(layer + GL_CTRL3);
> +       val |= GL_SCALER_BYPASS_MODE;   /* set for not bypass */
> +       writel(val, layer + GL_CTRL3);
> +
> +       zx_gl_rsz_setup(zplane, src_w, src_h, dst_w, dst_h);
> +
> +       /* Enable HBSC block */
> +       val = readl(hbsc + HBSC_CTRL0);
> +       val |= HBSC_CTRL_EN;
> +       writel(val, hbsc + HBSC_CTRL0);
> +
> +       zx_gl_set_update(zplane);
> +}
> +
> +static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs = {
> +       .atomic_check = zx_gl_plane_atomic_check,
> +       .atomic_update = zx_gl_plane_atomic_update,
> +};
> +
> +static void zx_plane_destroy(struct drm_plane *plane)
> +{
> +       drm_plane_helper_disable(plane);
> +       drm_plane_cleanup(plane);
> +}
> +
> +static const struct drm_plane_funcs zx_plane_funcs = {
> +       .update_plane = drm_atomic_helper_update_plane,
> +       .disable_plane = drm_atomic_helper_disable_plane,
> +       .destroy = zx_plane_destroy,
> +       .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 void zx_plane_hbsc_init(struct zx_plane *zplane)
> +{
> +       void __iomem *hbsc = zplane->hbsc;
> +
> +       /*
> +        *  Initialize HBSC block with a sane configuration per recommedation
> +        *  from ZTE BSP code.
> +        */
> +       writel(0x200, hbsc + HBSC_SATURATION);
> +       writel(0x0, hbsc + HBSC_HUE);
> +       writel(0x0, hbsc + HBSC_BRIGHT);
> +       writel(0x200, hbsc + HBSC_CONTRAST);
> +
> +       writel((0x3ac << 16) | 0x40, hbsc + HBSC_THRESHOLD_COL1);
> +       writel((0x3c0 << 16) | 0x40, hbsc + HBSC_THRESHOLD_COL2);
> +       writel((0x3c0 << 16) | 0x40, hbsc + HBSC_THRESHOLD_COL3);
> +}
> +
> +struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
> +                               void __iomem *mmio, struct zx_layer_data *data,
> +                               enum drm_plane_type type)
> +{
> +       const struct drm_plane_helper_funcs *helper;
> +       struct zx_plane *zplane;
> +       struct drm_plane *plane;
> +       const uint32_t *formats;
> +       unsigned int format_count;
> +       int ret;
> +
> +       zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL);
> +       if (!zplane)
> +               return ERR_PTR(-ENOMEM);
> +
> +       plane = &zplane->plane;
> +
> +       zplane->layer = mmio + data->layer_offset;
> +       zplane->hbsc = mmio + data->hbsc_offset;
> +       zplane->csc = mmio + data->csc_offset;
> +       zplane->rsz = mmio + data->rsz_offset;
> +
> +       zx_plane_hbsc_init(zplane);
> +
> +       switch (type) {
> +       case DRM_PLANE_TYPE_PRIMARY:
> +               helper = &zx_gl_plane_helper_funcs;
> +               formats = gl_formats;
> +               format_count = ARRAY_SIZE(gl_formats);
> +               break;
> +       case DRM_PLANE_TYPE_OVERLAY:
> +               /* TODO: add video layer (vl) support */
> +               break;
> +       default:
> +               return ERR_PTR(-ENODEV);
> +       }
> +
> +       ret = drm_universal_plane_init(drm, plane, VOU_CRTC_MASK,
> +                                      &zx_plane_funcs, formats, format_count,
> +                                      type, NULL);
> +       if (ret)
> +               return ERR_PTR(ret);
> +
> +       drm_plane_helper_add(plane, helper);
> +
> +       return plane;
> +}
> diff --git a/drivers/gpu/drm/zte/zx_plane.h b/drivers/gpu/drm/zte/zx_plane.h
> new file mode 100644
> index 000000000000..84a4e32cfc24
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_plane.h
> @@ -0,0 +1,26 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#ifndef __ZX_PLANE_H__
> +#define __ZX_PLANE_H__
> +
> +struct zx_layer_data {
> +       u16 layer_offset;
> +       u16 csc_offset;
> +       u16 hbsc_offset;
> +       u16 rsz_offset;
> +};
> +
> +struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
> +                               void __iomem *mmio, struct zx_layer_data *data,
> +                               enum drm_plane_type type);
> +void zx_plane_set_update(struct drm_plane *plane);
> +
> +#endif /* __ZX_PLANE_H__ */
> --
> 1.9.1
>



-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply

* [PATCH v3] gpio: Added zynq specific check for special pins on bank zero
From: Nava kishore Manne @ 2016-09-23 11:26 UTC (permalink / raw)
  To: linux-arm-kernel

From: Nava kishore Manne <nava.manne@xilinx.com>

This patch adds zynq specific check for bank 0 pins 7 and 8
are special and cannot be used as inputs

Signed-off-by: Nava kishore Manne <navam@xilinx.com>
Reported-by: Jonas Karlsson <Jonas.d.karlsson@gmail.com>
Acked-by: S?ren Brinkmann <soren.brinkmann@xilinx.com>
Acked-by: Michal Simek <michal.simek@xilinx.com>
---
Changes for v3:
		-Fixed some minor comments.
Changes for v2:
                -Removed un-used quirks for zynqmp.

 drivers/gpio/gpio-zynq.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index e72794e..6b4d10d 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -96,6 +96,9 @@
 /* GPIO upper 16 bit mask */
 #define ZYNQ_GPIO_UPPER_MASK 0xFFFF0000
 
+/* For GPIO quirks */
+#define ZYNQ_GPIO_QUIRK_FOO	BIT(0)
+
 /**
  * struct zynq_gpio - gpio device private data structure
  * @chip:	instance of the gpio_chip
@@ -122,6 +125,7 @@ struct zynq_gpio {
 */
 struct zynq_platform_data {
 	const char *label;
+	u32 quirks;
 	u16 ngpio;
 	int max_bank;
 	int bank_min[ZYNQMP_GPIO_MAX_BANK];
@@ -238,13 +242,19 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
 static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
 {
 	u32 reg;
+	bool is_zynq_gpio;
 	unsigned int bank_num, bank_pin_num;
 	struct zynq_gpio *gpio = gpiochip_get_data(chip);
 
+	is_zynq_gpio = gpio->p_data->quirks & ZYNQ_GPIO_QUIRK_FOO;
 	zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
 
-	/* bank 0 pins 7 and 8 are special and cannot be used as inputs */
-	if (bank_num == 0 && (bank_pin_num == 7 || bank_pin_num == 8))
+	/*
+	 * On zynq bank 0 pins 7 and 8 are special and cannot be used
+	 * as inputs.
+	 */
+	if (is_zynq_gpio && bank_num == 0 &&
+		(bank_pin_num == 7 || bank_pin_num == 8))
 		return -EINVAL;
 
 	/* clear the bit in direction mode reg to set the pin as input */
@@ -627,6 +637,7 @@ static const struct zynq_platform_data zynqmp_gpio_def = {
 
 static const struct zynq_platform_data zynq_gpio_def = {
 	.label = "zynq_gpio",
+	.quirks = ZYNQ_GPIO_QUIRK_FOO,
 	.ngpio = ZYNQ_GPIO_NR_GPIOS,
 	.max_bank = ZYNQ_GPIO_MAX_BANK,
 	.bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(),
-- 
2.1.2

^ permalink raw reply related

* [PATCH v4] devicetree: bindings: uart: Add new compatible string for ZynqMP
From: Nava kishore Manne @ 2016-09-23 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

From: Nava kishore Manne <nava.manne@xilinx.com>

This patch Adds the new compatible string for ZynqMP.

Signed-off-by: Nava kishore Manne <navam@xilinx.com>
---
Changes for v4:
		-Modified the ChangeLog comment.
Changes for v3:
		-Added changeLog comment.
Changes for v2:
                -None

 Documentation/devicetree/bindings/serial/cdns,uart.txt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/serial/cdns,uart.txt b/Documentation/devicetree/bindings/serial/cdns,uart.txt
index a3eb154..4fe7aae 100644
--- a/Documentation/devicetree/bindings/serial/cdns,uart.txt
+++ b/Documentation/devicetree/bindings/serial/cdns,uart.txt
@@ -1,7 +1,8 @@
 Binding for Cadence UART Controller
 
 Required properties:
-- compatible : should be "cdns,uart-r1p8", or "xlnx,xuartps"
+- compatible : should be "cdns,uart-r1p8", or "xlnx,xuartps" for Zynq and
+               "cdns,uart-r1p12" for Zynq Ultrascale+ MPSoC uart controller.
 - reg: Should contain UART controller registers location and length.
 - interrupts: Should contain UART controller interrupts.
 - clocks: Must contain phandles to the UART clocks
-- 
2.1.2

^ permalink raw reply related

* [PATCH v6 1/7] arm/arm64: vgic-new: Implement support for userspace access
From: Christoffer Dall @ 2016-09-23 11:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <57E4FAEE.3070003@arm.com>

On Fri, Sep 23, 2016 at 10:50:38AM +0100, Marc Zyngier wrote:
> On 22/09/16 15:01, Vijay Kilari wrote:
> > On Thu, Sep 22, 2016 at 5:38 PM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> >> On 20/09/16 07:12, vijay.kilari at gmail.com wrote:
> >>> From: Vijaya Kumar K <Vijaya.Kumar@cavium.com>
> >>> +static int vgic_uaccess_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
> >>> +                          gpa_t addr, u32 *val)
> >>> +{
> >>> +     struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev);
> >>> +     const struct vgic_register_region *region;
> >>> +     struct kvm_vcpu *r_vcpu;
> >>> +
> >>> +     region = vgic_get_mmio_region(iodev, addr, sizeof(u32));
> >>> +     if (!region) {
> >>> +             *val = 0;
> >>> +             return 0;
> >>
> >> This is not the previous semantic of vgic_uaccess, and I cannot see why
> >> blindly ignoring an access to an undefined region would be acceptable.
> >> What am I missing?
> > 
> > AFAIK, the vgic_uaccess is not making any check on undefined region/register.
> > However, dispatch_mmio_read/write are returning 0 if check of region is failed
> 
> Hmmm. Fair enough. I don't really like it, but that's something for
> another day.
> 
Agreed, we should raise an error in that case, but it's independent of
this series.

-Christoffer

^ permalink raw reply

* [PATCH v6] KVM: arm/arm64: Route vtimer events to user space
From: Alexander Graf @ 2016-09-23 11:07 UTC (permalink / raw)
  To: linux-arm-kernel

We have 2 modes for dealing with interrupts in the ARM world. We can either
handle them all using hardware acceleration through the vgic or we can emulate
a gic in user space and only drive CPU IRQ pins from there.

Unfortunately, when driving IRQs from user space, we never tell user space
about timer events that may result in interrupt line state changes, so we
lose out on timer events if we run with user space gic emulation.

This patch fixes that by exposing kvm's view of the vtimer irq line to user
space and giving it a chance to sync that when it changes.

With this patch I can successfully run edk2 and Linux with user space gic
emulation.

Signed-off-by: Alexander Graf <agraf@suse.de>

---

v1 -> v2:

  - Add back curly brace that got lost

v2 -> v3:

  - Split into patch set

v3 -> v4:

  - Improve documentation

v4 -> v5:

  - Rewrite to use pending state sync in sregs (marc)
  - Remove redundant checks of vgic_initialized()

v5 -> v6:

  - s/pending/asserted/
  - split kvm_timer_flush_hwstate function
  - remove user_timer_asserted
  - rename kernel_timer_asserted to timer_irq_level
  - exit only once per level change
  - qemu tree to try this out: https://github.com/agraf/qemu.git no-kvm-irqchip-for-v6
---
 Documentation/virtual/kvm/api.txt |  27 ++++++++
 arch/arm/include/uapi/asm/kvm.h   |   2 +
 arch/arm/kvm/arm.c                |  26 ++++----
 arch/arm64/include/uapi/asm/kvm.h |   2 +
 include/kvm/arm_arch_timer.h      |   4 +-
 include/uapi/linux/kvm.h          |   6 ++
 virt/kvm/arm/arch_timer.c         | 133 +++++++++++++++++++++++++++++++-------
 7 files changed, 164 insertions(+), 36 deletions(-)

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 739db9a..20ab558 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -3928,3 +3928,30 @@ In order to use SynIC, it has to be activated by setting this
 capability via KVM_ENABLE_CAP ioctl on the vcpu fd. Note that this
 will disable the use of APIC hardware virtualization even if supported
 by the CPU, as it's incompatible with SynIC auto-EOI behavior.
+
+8.3 KVM_CAP_ARM_TIMER
+
+Architectures: arm, arm64
+This capability, if KVM_CHECK_EXTENSION indicates that it is available and no
+in-kernel interrupt controller is in use, means that that the kernel populates
+the vcpu's run->s.regs.timer_irq_level field with timers that are currently
+considered asserted by kvm.
+
+If active, kvm guarantees at least one exit happens after it detects an
+assertion change. This exit could either be a KVM_EXIT_INTR or any other exit
+event, like KVM_EXIT_MMIO. This way kvm gives user space a chance to update its
+own interrupt asserted status. This usually involves triggering an interrupt
+line on a user space emulated interrupt controller, so user space should always
+check the state of run->s.regs.timer_irq_level on every kvm exit.
+
+The field run->s.regs.timer_irq_level is available independent of
+run->kvm_valid_regs or run->kvm_dirty_regs bits. If no in-kernel interrupt
+controller is used and the capability exists, it will always be available and
+populated.
+
+Currently the following bits are defined for the timer_irq_level bitmap:
+
+    KVM_ARM_TIMER_VTIMER  -  virtual timer
+
+Future versions of kvm may implement additional timer events. These will get
+indicated by additional KVM_CAP extensions.
diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h
index a2b3eb3..ad61b4f 100644
--- a/arch/arm/include/uapi/asm/kvm.h
+++ b/arch/arm/include/uapi/asm/kvm.h
@@ -105,6 +105,8 @@ struct kvm_debug_exit_arch {
 };
 
 struct kvm_sync_regs {
+	/* Used with KVM_CAP_ARM_TIMER */
+	u8 timer_irq_level;
 };
 
 struct kvm_arch_memory_slot {
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 75f130e..4bb1a22 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -187,6 +187,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_ARM_PSCI_0_2:
 	case KVM_CAP_READONLY_MEM:
 	case KVM_CAP_MP_STATE:
+	case KVM_CAP_ARM_TIMER:
 		r = 1;
 		break;
 	case KVM_CAP_COALESCED_MMIO:
@@ -474,13 +475,7 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
 			return ret;
 	}
 
-	/*
-	 * Enable the arch timers only if we have an in-kernel VGIC
-	 * and it has been properly initialized, since we cannot handle
-	 * interrupts from the virtual timer with a userspace gic.
-	 */
-	if (irqchip_in_kernel(kvm) && vgic_initialized(kvm))
-		ret = kvm_timer_enable(vcpu);
+	ret = kvm_timer_enable(vcpu);
 
 	return ret;
 }
@@ -549,7 +544,7 @@ static int kvm_vcpu_initialized(struct kvm_vcpu *vcpu)
  */
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
-	int ret;
+	int ret, timer_ret;
 	sigset_t sigsaved;
 
 	if (unlikely(!kvm_vcpu_initialized(vcpu)))
@@ -588,7 +583,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		 */
 		preempt_disable();
 		kvm_pmu_flush_hwstate(vcpu);
-		kvm_timer_flush_hwstate(vcpu);
+		timer_ret = kvm_timer_flush_hwstate(vcpu);
 		kvm_vgic_flush_hwstate(vcpu);
 
 		local_irq_disable();
@@ -596,7 +591,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		/*
 		 * Re-check atomic conditions
 		 */
-		if (signal_pending(current)) {
+		if (timer_ret || signal_pending(current)) {
 			ret = -EINTR;
 			run->exit_reason = KVM_EXIT_INTR;
 		}
@@ -659,13 +654,22 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		 * interrupt line.
 		 */
 		kvm_pmu_sync_hwstate(vcpu);
-		kvm_timer_sync_hwstate(vcpu);
+		timer_ret = kvm_timer_sync_hwstate(vcpu);
 
 		kvm_vgic_sync_hwstate(vcpu);
 
 		preempt_enable();
 
 		ret = handle_exit(vcpu, run, ret);
+
+		if ((ret == 1) && timer_ret) {
+			/*
+			 * We have to exit straight away to ensure that we only
+			 * ever notify user space once about a level change
+			 */
+			ret = -EINTR;
+			run->exit_reason = KVM_EXIT_INTR;
+		}
 	}
 
 	if (vcpu->sigset_active)
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 3051f86..411d62a 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -143,6 +143,8 @@ struct kvm_debug_exit_arch {
 #define KVM_GUESTDBG_USE_HW		(1 << 17)
 
 struct kvm_sync_regs {
+	/* Used with KVM_CAP_ARM_TIMER */
+	u8 timer_irq_level;
 };
 
 struct kvm_arch_memory_slot {
diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h
index dda39d8..dd0d649 100644
--- a/include/kvm/arm_arch_timer.h
+++ b/include/kvm/arm_arch_timer.h
@@ -63,8 +63,8 @@ void kvm_timer_init(struct kvm *kvm);
 int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
 			 const struct kvm_irq_level *irq);
 void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu);
-void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu);
-void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu);
+int kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu);
+int kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu);
 void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu);
 
 u64 kvm_arm_timer_get_reg(struct kvm_vcpu *, u64 regid);
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 300ef25..10d63c5 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -870,6 +870,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_S390_USER_INSTR0 130
 #define KVM_CAP_MSI_DEVID 131
 #define KVM_CAP_PPC_HTM 132
+#define KVM_CAP_ARM_TIMER 133
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -1327,4 +1328,9 @@ struct kvm_assigned_msix_entry {
 #define KVM_X2APIC_API_USE_32BIT_IDS            (1ULL << 0)
 #define KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK  (1ULL << 1)
 
+/* Available with KVM_CAP_ARM_TIMER */
+
+/* Bits for run->s.regs.{user,kernel}_timer_asserted */
+#define KVM_ARM_TIMER_VTIMER		(1 << 0)
+
 #endif /* __LINUX_KVM_H */
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index 4309b60..3612fa0 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -166,26 +166,57 @@ bool kvm_timer_should_fire(struct kvm_vcpu *vcpu)
 	return cval <= now;
 }
 
-static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level)
+/*
+ * Synchronize the timer IRQ state with the interrupt controller.
+ *
+ * Returns:
+ *
+ *    0  - success
+ *    1  - need exit to user space
+ */
+static int kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level)
 {
 	int ret;
 	struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
 
-	BUG_ON(!vgic_initialized(vcpu->kvm));
-
 	timer->active_cleared_last = false;
 	timer->irq.level = new_level;
-	trace_kvm_timer_update_irq(vcpu->vcpu_id, timer->irq.irq,
+	trace_kvm_timer_update_irq(vcpu->vcpu_id, host_vtimer_irq,
 				   timer->irq.level);
-	ret = kvm_vgic_inject_mapped_irq(vcpu->kvm, vcpu->vcpu_id,
-					 timer->irq.irq,
-					 timer->irq.level);
-	WARN_ON(ret);
+
+	if (irqchip_in_kernel(vcpu->kvm)) {
+		BUG_ON(!vgic_initialized(vcpu->kvm));
+
+		/* Fire the timer in the VGIC */
+		ret = kvm_vgic_inject_mapped_irq(vcpu->kvm, vcpu->vcpu_id,
+						 timer->irq.irq,
+						 timer->irq.level);
+
+		WARN_ON(ret);
+	} else {
+		struct kvm_sync_regs *regs = &vcpu->run->s.regs;
+
+		/* Populate the timer bitmap for user space */
+		regs->timer_irq_level &= ~KVM_ARM_TIMER_VTIMER;
+		if (new_level)
+			regs->timer_irq_level |= KVM_ARM_TIMER_VTIMER;
+
+		/* Exit to user space to notify it about the line change */
+		return 1;
+	}
+
+	return 0;
 }
 
 /*
  * Check if there was a change in the timer state (should we raise or lower
  * the line level to the GIC).
+ *
+ * Returns:
+ *
+ *   <0  - error
+ *    0  - success
+ *    1  - need exit to user space
  */
 static int kvm_timer_update_state(struct kvm_vcpu *vcpu)
 {
@@ -197,11 +228,12 @@ static int kvm_timer_update_state(struct kvm_vcpu *vcpu)
 	 * because the guest would never see the interrupt.  Instead wait
 	 * until we call this function from kvm_timer_flush_hwstate.
 	 */
-	if (!vgic_initialized(vcpu->kvm) || !timer->enabled)
+	if ((irqchip_in_kernel(vcpu->kvm) && !vgic_initialized(vcpu->kvm)) ||
+	    !timer->enabled)
 		return -ENODEV;
 
 	if (kvm_timer_should_fire(vcpu) != timer->irq.level)
-		kvm_timer_update_irq(vcpu, !timer->irq.level);
+		return kvm_timer_update_irq(vcpu, !timer->irq.level);
 
 	return 0;
 }
@@ -242,22 +274,12 @@ void kvm_timer_unschedule(struct kvm_vcpu *vcpu)
 	timer_disarm(timer);
 }
 
-/**
- * kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu
- * @vcpu: The vcpu pointer
- *
- * Check if the virtual timer has expired while we were running in the host,
- * and inject an interrupt if that was the case.
- */
-void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
+static void kvm_timer_flush_hwstate_vgic(struct kvm_vcpu *vcpu)
 {
 	struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
 	bool phys_active;
 	int ret;
 
-	if (kvm_timer_update_state(vcpu))
-		return;
-
 	/*
 	* If we enter the guest with the virtual input level to the VGIC
 	* asserted, then we have already told the VGIC what we need to, and
@@ -309,14 +331,69 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
 	timer->active_cleared_last = !phys_active;
 }
 
+static void kvm_timer_flush_hwstate_user(struct kvm_vcpu *vcpu)
+{
+	struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+
+	/*
+	 * As long as the timer is asserted, we do not need to get new host
+	 * timer events to know whether the line is still up. Since the GIC
+	 * is handled in user space, we will get an exit on EOI, so we can
+	 * resynchronize the status there.
+	 */
+	if (timer->irq.level)
+		disable_percpu_irq(host_vtimer_irq);
+	else
+		enable_percpu_irq(host_vtimer_irq, 0);
+}
+
+/**
+ * kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu
+ * @vcpu: The vcpu pointer
+ *
+ * Check if the virtual timer has expired while we were running in the host,
+ * and inject an interrupt if that was the case.
+ *
+ * Returns:
+ *
+ *    0  - success
+ *    1  - need exit to user space
+ */
+int kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
+{
+	switch (kvm_timer_update_state(vcpu)) {
+	case 0:
+		/* All is good, we can sync */
+		break;
+	case 1:
+		/* We need to exit to user space */
+		return 1;
+	default:
+		/* An error occured, skip the sync */
+		return 0;
+	}
+
+	if (irqchip_in_kernel(vcpu->kvm))
+		kvm_timer_flush_hwstate_vgic(vcpu);
+	else
+		kvm_timer_flush_hwstate_user(vcpu);
+
+	return 0;
+}
+
 /**
  * kvm_timer_sync_hwstate - sync timer state from cpu
  * @vcpu: The vcpu pointer
  *
  * Check if the virtual timer has expired while we were running in the guest,
  * and inject an interrupt if that was the case.
+ *
+ * Returns:
+ *
+ *    0  - success
+ *    1  - need exit to user space
  */
-void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
+int kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
 {
 	struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
 
@@ -326,7 +403,12 @@ void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
 	 * The guest could have modified the timer registers or the timer
 	 * could have expired, update the timer state.
 	 */
-	kvm_timer_update_state(vcpu);
+	if (kvm_timer_update_state(vcpu) == 1) {
+		/* We should go back to user space */
+		return 1;
+	}
+
+	return 0;
 }
 
 int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
@@ -479,6 +561,10 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
 	if (timer->enabled)
 		return 0;
 
+	/* No need to route physical IRQs when we don't use the vgic */
+	if (!irqchip_in_kernel(vcpu->kvm))
+		goto no_vgic;
+
 	/*
 	 * Find the physical IRQ number corresponding to the host_vtimer_irq
 	 */
@@ -502,6 +588,7 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
 	if (ret)
 		return ret;
 
+no_vgic:
 
 	/*
 	 * There is a potential race here between VCPUs starting for the first
-- 
1.8.5.6

^ permalink raw reply related

* [PATCH V6 3/5] PCI: thunder-pem: Allow to probe PEM-specific register range for ACPI case
From: Gabriele Paoloni @ 2016-09-23 10:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160923101145.GA31120@red-moon>

Hi Lorenzo

> -----Original Message-----
> From: linux-kernel-owner at vger.kernel.org [mailto:linux-kernel-
> owner at vger.kernel.org] On Behalf Of Lorenzo Pieralisi
> Sent: 23 September 2016 11:12
> To: Bjorn Helgaas
> Cc: Gabriele Paoloni; Ard Biesheuvel; Tomasz Nowicki; David Daney; Will
> Deacon; Catalin Marinas; Rafael Wysocki; Arnd Bergmann; Hanjun Guo;
> Sinan Kaya; Jayachandran C; Christopher Covington; Duc Dang; Robert
> Richter; Marcin Wojtas; Liviu Dudau; Wangyijing; Mark Salter; linux-
> pci at vger.kernel.org; linux-arm-kernel at lists.infradead.org; Linaro ACPI
> Mailman List; Jon Masters; Andrea Gallo; Jeremy Linton; liudongdong
> (C); Jeff Hugo; linux-acpi at vger.kernel.org; linux-
> kernel at vger.kernel.org; Rafael J. Wysocki; rui.zhang at intel.com
> Subject: Re: [PATCH V6 3/5] PCI: thunder-pem: Allow to probe PEM-
> specific register range for ACPI case
> 
> [+ Zhang Rui]
> 
> On Thu, Sep 22, 2016 at 05:10:42PM -0500, Bjorn Helgaas wrote:
> > On Thu, Sep 22, 2016 at 01:31:01PM -0500, Bjorn Helgaas wrote:
> > > On Thu, Sep 22, 2016 at 01:44:46PM +0100, Lorenzo Pieralisi wrote:
> > > > On Thu, Sep 22, 2016 at 11:10:13AM +0000, Gabriele Paoloni wrote:
> > > > > Hi Lorenzo, Bjorn
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Lorenzo Pieralisi [mailto:lorenzo.pieralisi at arm.com]
> > > > > > Sent: 22 September 2016 10:50
> > > > > > To: Bjorn Helgaas
> > > > > > Cc: Ard Biesheuvel; Tomasz Nowicki; David Daney; Will Deacon;
> Catalin
> > > > > > Marinas; Rafael Wysocki; Arnd Bergmann; Hanjun Guo; Sinan
> Kaya;
> > > > > > Jayachandran C; Christopher Covington; Duc Dang; Robert
> Richter; Marcin
> > > > > > Wojtas; Liviu Dudau; Wangyijing; Mark Salter; linux-
> > > > > > pci at vger.kernel.org; linux-arm-kernel at lists.infradead.org;
> Linaro ACPI
> > > > > > Mailman List; Jon Masters; Andrea Gallo; Jeremy Linton;
> liudongdong
> > > > > > (C); Gabriele Paoloni; Jeff Hugo; linux-acpi at vger.kernel.org;
> linux-
> > > > > > kernel at vger.kernel.org; Rafael J. Wysocki
> > > > > > Subject: Re: [PATCH V6 3/5] PCI: thunder-pem: Allow to probe
> PEM-
> > > > > > specific register range for ACPI case
> > > > > >
> > > > > > On Wed, Sep 21, 2016 at 01:04:57PM -0500, Bjorn Helgaas
> wrote:
> > > > > > > On Wed, Sep 21, 2016 at 03:05:49PM +0100, Lorenzo Pieralisi
> wrote:
> > > > > > > > On Tue, Sep 20, 2016 at 02:17:44PM -0500, Bjorn Helgaas
> wrote:
> > > > > > > > > On Tue, Sep 20, 2016 at 04:09:25PM +0100, Ard
> Biesheuvel wrote:
> > > > > > > >
> > > > > > > > [...]
> > > > > > > >
> > > > > > > > > > None of these platforms can be fixed entirely in
> software, and
> > > > > > given
> > > > > > > > > > that we will not be adding quirks for new broken
> hardware, we
> > > > > > should
> > > > > > > > > > ask ourselves whether having two versions of a quirk,
> i.e., one
> > > > > > for
> > > > > > > > > > broken hardware + currently shipping firmware, and
> one for the
> > > > > > same
> > > > > > > > > > broken hardware with fixed firmware is really an
> improvement
> > > > > > over what
> > > > > > > > > > has been proposed here.
> > > > > > > > >
> > > > > > > > > We're talking about two completely different types of
> quirks:
> > > > > > > > >
> > > > > > > > >   1) MCFG quirks to use memory-mapped config space that
> doesn't
> > > > > > quite
> > > > > > > > >      conform to the ECAM model in the PCIe spec, and
> > > > > > > > >
> > > > > > > > >   2) Some yet-to-be-determined method to describe
> address space
> > > > > > > > >      consumed by a bridge.
> > > > > > > > >
> > > > > > > > > The first two patches of this series are a nice
> implementation
> > > > > > for 1).
> > > > > > > > > The third patch (ThunderX-specific) is one possibility
> for 2),
> > > > > > but I
> > > > > > > > > don't like it because there's no way for generic
> software like
> > > > > > the
> > > > > > > > > ACPI core to discover these resources.
> > > > > > > >
> > > > > > > > Ok, so basically this means that to implement (2) we need
> to assign
> > > > > > > > some sort of _HID to these quirky PCI bridges (so that we
> know what
> > > > > > > > device they represent and we can retrieve their _CRS). I
> take from
> > > > > > > > this discussion that the goal is to make sure that all
> non-config
> > > > > > > > resources have to be declared through _CRS device
> objects, which is
> > > > > > > > fine but that requires a FW update (unless we can
> fabricate ACPI
> > > > > > > > devices and corresponding _CRS in the kernel whenever we
> match a
> > > > > > > > given MCFG table signature).
> > > > > > >
> > > > > > > All resources consumed by ACPI devices should be declared
> through
> > > > > > > _CRS.  If you want to fabricate ACPI devices or _CRS via
> kernel
> > > > > > > quirks, that's fine with me.  This could be triggered via
> MCFG
> > > > > > > signature, DMI info, host bridge _HID, etc.
> > > > > >
> > > > > > I think the PNP quirk approach + PNP0c02 resource put forward
> by Gab
> > > > > > is enough.
> > > > >
> > > > > Great thanks as we take a final decision I will ask Dogndgong
> to submit
> > > > > another RFC based on this approach
> > > > >
> > > > > >
> > > > > > > > We discussed this already and I think we should make a
> decision:
> > > > > > > >
> > > > > > > > http://lists.infradead.org/pipermail/linux-arm-
> kernel/2016-
> > > > > > March/414722.html
> > > > > > > >
> > > > > > > > > > > I'd like to step back and come up with some
> understanding of
> > > > > > how
> > > > > > > > > > > non-broken firmware *should* deal with this issue.
> Then, if
> > > > > > we *do*
> > > > > > > > > > > work around this particular broken firmware in the
> kernel, it
> > > > > > would be
> > > > > > > > > > > nice to do it in a way that fits in with that
> understanding.
> > > > > > > > > > >
> > > > > > > > > > > For example, if a companion ACPI device is the
> preferred
> > > > > > solution, an
> > > > > > > > > > > ACPI quirk could fabricate a device with the
> required
> > > > > > resources.  That
> > > > > > > > > > > would address the problem closer to the source and
> make it
> > > > > > more likely
> > > > > > > > > > > that the rest of the system will work correctly:
> /proc/iomem
> > > > > > could
> > > > > > > > > > > make sense, things that look at _CRS generically
> would work
> > > > > > (e.g,
> > > > > > > > > > > /sys/, an admittedly hypothetical "lsacpi", etc.)
> > > > > > > > > > >
> > > > > > > > > > > Hard-coding stuff in drivers is a point solution
> that doesn't
> > > > > > provide
> > > > > > > > > > > any guidance for future platforms and makes it
> likely that
> > > > > > the hack
> > > > > > > > > > > will get copied into even more drivers.
> > > > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > OK, I see. But the guidance for future platforms
> should be 'do
> > > > > > not
> > > > > > > > > > rely on quirks', and what I am arguing here is that
> the more we
> > > > > > polish
> > > > > > > > > > up this code and make it clean and reusable, the more
> likely it
> > > > > > is
> > > > > > > > > > that will end up getting abused by new broken
> hardware that we
> > > > > > set out
> > > > > > > > > > to reject entirely in the first place.
> > > > > > > > > >
> > > > > > > > > > So of course, if the quirk involves claiming
> resources, let's
> > > > > > make
> > > > > > > > > > sure that this occurs in the cleanest and most
> compliant way
> > > > > > possible.
> > > > > > > > > > But any factoring/reuse concerns other than for the
> current
> > > > > > crop of
> > > > > > > > > > broken hardware should be avoided imo.
> > > > > > > > >
> > > > > > > > > If future hardware is completely ECAM-compliant and we
> don't need
> > > > > > any
> > > > > > > > > more MCFG quirks, that would be great.
> > > > > > > >
> > > > > > > > Yes.
> > > > > > > >
> > > > > > > > > But we'll still need to describe that memory-mapped
> config space
> > > > > > > > > somewhere.  If that's done with PNP0C02 or similar
> devices (as is
> > > > > > done
> > > > > > > > > on my x86 laptop), we'd be all set.
> > > > > > > >
> > > > > > > > I am not sure I understand what you mean here. Are you
> referring
> > > > > > > > to MCFG regions reported as PNP0c02 resources through its
> _CRS ?
> > > > > > >
> > > > > > > Yes.  PCI Firmware Spec r3.0, Table 4-2, note 2 says
> address ranges
> > > > > > > reported via MCFG or _CBA should be reserved by _CRS of a
> PNP0C02
> > > > > > > device.
> > > > > >
> > > > > > Ok, that's agreed. It goes without saying that since you are
> quoting
> > > > > > the PCI spec, if FW fails to report MCFG regions in a PNP0c02
> device
> > > > > > _CRS I will consider that a FW bug.
> > > > > >
> > > > > > > > IIUC PNP0C02 is a reservation mechanism, but it does not
> help us
> > > > > > > > associate its _CRS to a specific PCI host bridge
> instance, right ?
> > > > > > >
> > > > > > > Gab proposed a hierarchy that *would* associate a PNP0C02
> device with
> > > > > > > a PCI bridge:
> > > > > > >
> > > > > > >   Device (PCI1) {
> > > > > > >     Name (_HID, "HISI0080") // PCI Express Root Bridge
> > > > > > >     Name (_CID, "PNP0A03") // Compatible PCI Root Bridge
> > > > > > >     Method (_CRS, 0, Serialized) { // Root complex
> resources
> > > > > > (windows) }
> > > > > > >     Device (RES0) {
> > > > > > >       Name (_HID, "HISI0081") // HiSi PCIe RC config base
> address
> > > > > > >       Name (_CID, "PNP0C02")  // Motherboard reserved
> resource
> > > > > > >       Name (_CRS, ResourceTemplate () { ... }
> > > > > > >     }
> > > > > > >   }
> > > > > > >
> > > > > > > That's a possibility.  The PCI Firmware Spec suggests
> putting RES0 at
> > > > > > > the root (under \_SB), but I don't know why.
> > > > > > >
> > > > > > > Putting it at the root means we couldn't generically
> associate it
> > > > > > with
> > > > > > > a bridge, although I could imagine something like this:
> > > > > > >
> > > > > > >   Device (RES1) {
> > > > > > >     Name (_HID, "HISI0081") // HiSi PCIe RC config base
> address
> > > > > > >     Name (_CID, "PNP0C02")  // Motherboard reserved
> resource
> > > > > > >     Name (_CRS, ResourceTemplate () { ... }
> > > > > > >     Method (BRDG) { "PCI1" }  // hand-wavy ASL
> > > > > > >   }
> > > > > > >   Device (PCI1) {
> > > > > > >     Name (_HID, "HISI0080") // PCI Express Root Bridge
> > > > > > >     Name (_CID, "PNP0A03") // Compatible PCI Root Bridge
> > > > > > >     Method (_CRS, 0, Serialized) { // Root complex
> resources
> > > > > > (windows) }
> > > > > > >   }
> > > > > > >
> > > > > > > Where you could search PNP0C02 devices for a cookie that
> matched the
> > > > > > > host bridge.o
> > > > > >
> > > > > > Ok, I am fine with both and I think we are converging, but
> the way
> > > > > > to solve this problem has to be uniform for all ARM partners
> (and
> > > > > > not only ARM). Two points here:
> > > > > >
> > > > > > 1) Adding a device/subdevice allows people to add a _CRS
> reporting the
> > > > > >    non-window bridge resources. Fine. It also allows people
> to chuck in
> > > > > >    there all sorts of _DSD properties to describe their PCI
> host bridge
> > > > > >    as it is done with DT properties (those _DSD can contain
> eg clocks
> > > > > >    etc.), this may be tempting (so that they can reuse the
> same DT
> > > > > >    driver and do not have to update their firmware) but I
> want to be
> > > > > >    clear here: that must not happen. So, a subdevice with a
> _CRS to
> > > > > >    report resources, yes, but it will stop there.
> > > > > > 2) It is unclear to me how to formalize the above. People
> should not
> > > > > >    write FW by reading the PCI mailing list, so these
> guidelines have
> > > > > > to
> > > > > >    be written, somehow. I do not want to standardize quirks,
> I want
> > > > > >    to prevent random ACPI table content, which is different.
> > > > > >    Should I report this to the ACPI spec working group ? If
> we do
> > > > > >    not do that everyone will go solve this problem as they
> deem fit.
> > > > > >
> > > > >
> > > > > Do we really need to formalize this?
> > > > >
> > > > > As we discussed in the Linaro call at the moment we have few
> vendors
> > > > > that need quirks and we want to avoid promoting/accepting
> quirks for
> > > > > the future.
> > > > >
> > > > > At the time of the call I think we decided to informally accept
> a set
> > > > > of quirks for the current platforms and reject any other quirk
> coming
> > > > > after a certain date/kernel version (this to be decided).
> > > > >
> > > > > I am not sure if there is a way to document/formalize a
> temporary
> > > > > exception from the rule...
> > > >
> > > > - (1) will be enforced.
> > >
> > > I'm not sure it's necessary or possible to enforce a "no future
> > > quirks" rule.  For one thing, there's already a pretty strong
> > > incentive to avoid quirks: if your hardware doesn't require quirks,
> > > it works with OSes already in the field.
> > >
> > > MCFG quirks allow us to use the generic ACPI pci_root.c driver even
> if
> > > the hardware doesn't support ECAM quite according to the spec.
> > >
> > > PNP0C02 usage is a workaround for the failure of the
> Consumer/Producer
> > > bit.  PNP0C02 quirks compensate for firmware that doesn't describe
> > > resource usage accurately.  It's possible the ACPI spec folks could
> > > come up with a better Consumer/Producer workaround, if that's
> needed.
> > > Apparently x86 hasn't needed it yet.
> > >
> > > If people add _DSD methods for clocks or whatnot, the hardware
> won't
> > > work with the generic pci_root.c driver, so there's already an
> > > incentive for avoiding them.  x86 has managed without such methods;
> > > arm64 should be able to do the same.
> >
> > Re-reading this, I'm afraid my response sounds a little dismissive,
> > and I feel like I'm missing some important information.  So I
> > apologize if I missed your whole point, Lorenzo.
> 
> No you are spot on, I just wanted to emphasize, given that we are
> adding an _HID and a subdevice, that developer should not be tempted
> to use it to match against a PCI host driver to reuse the DT code,
> we should not use the quirk mechanism as a backdoor to re-using DT
> drivers in ACPI context.
> 
> Anyway, there is a review process to spot these possible misuses,
> mine was just a heads-up, quirks will happen, I just do not want
> to wreak the standard ACPI PCI firmware model to support them.
> 
> Given that there are already PNP0c02 bindings out there where the
> PNP0c02 is used as in Gab's example:
> 
> https://patchwork.kernel.org/patch/4757111/
> 
> I think the only pending question I have is whether we are allowed
> to define a PNP0A03 subdevice with a _CRS resource space that is
> not contained in its parent _CRS, if we answer this question I
> think we are done.

FMU part of your question is answered in the PCI Firmware specs
https://members.pcisig.com/wg/PCI-SIG/document/download/8232

Where from note 2 of 4.1.2 I quote: 
"For most systems, the motherboard resource would appear at the root
of the ACPI namespace (under \_SB) in a node with a _HID of EISAID 
(PNP0C02), and the resources in this case should not be claimed in the
root PCI bus's _CRS"

My interpretation is that the resource claimed in the PNP0C02 node
must never be in the PNP0A03 _CRS.

Now about having the PNP0C02 node under \_SB or as a sub-device we see
that the note above points out that most of system have it under \_SB
but I read it as a quite relaxed condition....

BTW this is just my interpretation...

Thanks

Gab 


> 
> I will raise the PNP0c02 usage issue with the ASWG anyway.
> 
> Thanks !
> Lorenzo

^ permalink raw reply

* [PATCH V6 3/5] PCI: thunder-pem: Allow to probe PEM-specific register range for ACPI case
From: Tomasz Nowicki @ 2016-09-23 10:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921191845.GD20006@localhost>

Hi Bjorn,

On 21.09.2016 21:18, Bjorn Helgaas wrote:
> On Wed, Sep 21, 2016 at 11:58:22AM -0700, Duc Dang wrote:
>> On Wed, Sep 21, 2016 at 11:04 AM, Bjorn Helgaas <helgaas@kernel.org> wrote:
>>> On Wed, Sep 21, 2016 at 03:05:49PM +0100, Lorenzo Pieralisi wrote:
>
>>> The existing x86 practice is to use PNP0C02 devices for this purpose,
>>> and I think we should just follow that practice.
>>>
>>> ...
>>>
>>> My point is that the hard-coding should not be buried in a driver
>>> where it's invisible to the rest of the kernel.  If we hard-code it in
>>> a quirk that adds _CRS entries, then the kernel will work just like it
>>> would if the firmware had been correct in the first place.  The
>>> resource will appear in /sys/devices/pnp*/*/resources and /proc/iomem,
>>> and if we ever used _SRS to assign or move ACPI devices, we would know
>>> to avoid the bridge resource.
>>
>> Are you suggesting to add code similar to functions in
>> linux/drivers/pnp/quirks.c to declare/attach the additional resource
>> that the host need to have when the resource is not in MCFG table?
>
> Yes, but what I'm suggesting is actually a little stronger.  This has
> nothing to do with whether a resource is in the MCFG table or not.
>
> I'm suggesting ACPI firmware should always describe the resource.  If the
> firmware is defective and doesn't describe it, we should add a quirk in
> pnp/quirks.c to add a resource for it.
>

Thanks for pointers Bjorn.

ThunderX is the case where we cannot change firmware, also it has no 
PNP0c02 device in tables. So in order to use pnp/quirks.c we would have 
to fabricate PNP0c02 in kernel and then add quirk entry. I am looking 
for the best place to put such emulation code but it seems not trivial.

Thanks,
Tomasz

^ permalink raw reply

* [PATCH v2] gpio: Added zynq specific check for special pins on bank zero
From: Michal Simek @ 2016-09-23 10:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474625137-9022-1-git-send-email-navam@xilinx.com>

On 23.9.2016 12:05, Nava kishore Manne wrote:
> From: Nava kishore Manne <nava.manne@xilinx.com>
> 
> This patch adds zynq specific check for bank 0 pins 7 and 8
> are special and cannot be used as inputs
> 
> Signed-off-by: Nava kishore Manne <navam@xilinx.com>
> Acked-by: Michal Simek <michal.simek@xilinx.com>
> ---
> Changes for v2:
>                 -Removed un-used quirks for zynqmp.


You missed Soren's ACK and also Jonas reported by line.
Please add it to v3.

Thakns,
Michal

^ permalink raw reply

* [PATCH 0/2] ARM: Do not mess with CCI if booted from HYP
From: Lorenzo Pieralisi @ 2016-09-23 10:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474380833-30121-1-git-send-email-marc.zyngier@arm.com>

Hi Marc,

On Tue, Sep 20, 2016 at 03:13:51PM +0100, Marc Zyngier wrote:
> Some systems (such as the VExpress TC2) are built around a CCI-400,
> which is only accessible from secure mode. This obviously breaks if
> the system is booted in non-secure mode. Detecting non-secure is
> pretty hard to do, unless the system is booted from HYP.
> 
> This small patch series makes sure that:
> - The CCI ports are not accessed when the kernel is booted from HYP
> - MCPM (which depends on CCI) is not enabled on VExpress if booted
> from HYP.
> 
> This allows a multi_v7_defconfig kernel to be booted on a TC2 booting
> from HYP, instead of miserably dying very early on.

As discussed I came up with a different set of patches that
detect CCI ports status as reported by DT and prevent enabling
MCPM if the CCI ports are not under kernel control (eg they are
reported as "disabled" by DT since we booted in HYP).

I will post patches for review and test shortly.

Thanks for fixing it anyway,
Lorenzo

> 
> Marc Zyngier (2):
>   ARM: vexpress: Do not enable MCPM if booted from HYP
>   arm-cci: Do not probe the CCI ports if booted from HYP
> 
>  arch/arm/mach-vexpress/platsmp.c |  8 ++++++--
>  drivers/bus/arm-cci.c            | 11 +++++++++++
>  2 files changed, 17 insertions(+), 2 deletions(-)
> 
> -- 
> 2.1.4
> 

^ permalink raw reply

* [PATCH v2.1] arm64: kgdb: handle read-only text / modules
From: Catalin Marinas @ 2016-09-23 10:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160923094953.GC27704@e104818-lin.cambridge.arm.com>

On Fri, Sep 23, 2016 at 10:49:53AM +0100, Catalin Marinas wrote:
> On Fri, Sep 23, 2016 at 04:42:08PM +0900, AKASHI Takahiro wrote:
> > Handle read-only cases (CONFIG_DEBUG_RODATA/CONFIG_DEBUG_SET_MODULE_RONX)
> > by using aarch64_insn_write() instead of probe_kernel_write().
> > See how this works:
> >     commit 2f896d586610 ("arm64: use fixmap for text patching")
> > 
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > Reviewed-by: Mark Rutland <mark.rutland@arm.com>
> > Cc: Catalin Marinas <catalin.marinas@arm.com>
> > Cc: Will Deacon <will.deacon@arm.com>
> > Cc: Jason Wessel <jason.wessel@windriver.com>
> > Cc: <stable@vger.kernel.org> # 3.18-3.19: 2f896d5: arm64: use fixmap
> > Cc: <stable@vger.kernel.org> # 3.18-3.19: f6242ca: arm64: Fix text
> > Cc: <stable@vger.kernel.org> # 4.0-
> 
> Queued for 4.8 with a slight change in the last Cc: tag above:
> 
> Cc: <stable@vger.kernel.org> # 3.18.x-

I tried to apply this patch to 3.18, 4.1, 4.4, 4.7. It fails on all of
them with smaller or slightly larger conflicts.

So, I'll merge it in 4.8 without any "Cc: stable" tags, just a "Fixes:"
one for the commit introducing CONFIG_DEBUG_SET_MODULE_RONX (3.18). Once
the patch reaches mainline, could you please back-port and test it on
the stable kernel versions and send separate patches to
stable at vger.kernel.org?

Thanks.

-- 
Catalin

^ permalink raw reply

* [PATCH V3 2/4] ARM64 LPC: LPC driver implementation on Hip06
From: Gabriele Paoloni @ 2016-09-23 10:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1760643.vMTR5o5E9g@wuerfel>

Hi Arnd

> -----Original Message-----
> From: Arnd Bergmann [mailto:arnd at arndb.de]
> Sent: 23 September 2016 10:52
> To: zhichang.yuan
> Cc: Gabriele Paoloni; linux-arm-kernel at lists.infradead.org;
> devicetree at vger.kernel.org; lorenzo.pieralisi at arm.com; minyard at acm.org;
> linux-pci at vger.kernel.org; gregkh at linuxfoundation.org; John Garry;
> will.deacon at arm.com; linux-kernel at vger.kernel.org; Yuanzhichang;
> Linuxarm; xuwei (O); linux-serial at vger.kernel.org;
> benh at kernel.crashing.org; zourongrong at gmail.com; liviu.dudau at arm.com;
> kantyzc at 163.com
> Subject: Re: [PATCH V3 2/4] ARM64 LPC: LPC driver implementation on
> Hip06
> 
> On Friday, September 23, 2016 12:27:17 AM CEST zhichang.yuan wrote:
> > For this patch sketch, I have a question.
> > Do we call pci_address_to_pio in arch_of_address_to_pio to get the
> > corresponding logical IO port
> > for LPC??
> 
> 
> No, of course not, that would be silly:
> 
> The argument to pci_address_to_pio() is a phys_addr_t, and we we don't
> have one because there is no address associated with your PIO, that
> is the entire point of your driver!
> 
> Also, we already know the mapping because this is what the inb/outb
> workaround is looking at, so there is absolutely no reason to call it
> either.

Ok assume that we do not call pci_address_to_pio() for the ISA bus...
The LPC driver will register its phys address range in io_range_list,
then the IPMI driver probe will retrieve its physical address calling
of_address_to_resource and will use the indirect io to access this
address.

>From the perspective of the indirect IO function the input parameter
is an unsigned long addr that (now) can be either:
1) an IO token coming from a legacy pci device
2) a phys address that lives on the LPC bus

These are conceptually two separate address spaces (and actually they 
both start from 0).

If the input parameter can live on different address spaces that are
overlapped, even if I save the used LPC range in arm64_extio_ops->start/end
there is no way for the indirect IO to tell if the input parameter is
an I/O token or a phys address that belongs to LPC...  

Am I missing something?

Thanks

Gab

> 
> > If we don't, it seems the LPC specific IO address will conflict with
> PCI
> > host bridges' logical IO.
> >
> > Supposed our LPC populated the IO range from 0x100 to 0x3FF( this is
> > normal for ISA similar
> > devices), after arch_of_address_to_pio(), the r->start will be set as
> > 0x100, r->end will be set as
> > 0x3FF.  And if there is one PCI host bridge who request a IO window
> size
> > over 0x400 at the same
> > time, the  corresponding r->start and r->end will be set as 0x0,
> 0x3FF
> > after of_address_to_resource
> > for this host bridge.  Then the IO conflict happens.
> 
> You would still need to reserve some space in the io_range_list
> to avoid possible conflicts, which is a bit ugly with the current
> definition of pci_register_io_range, but I'm sure can be done.
> 
> One way I can think of would be to change pci_register_io_range()
> to just return the logical port number directly (it already
> knows it!), and pass an invalid physical address (e.g.
> #define ISA_WORKAROUND_IO_PORT_WINDOW -0x10000) into it for
> invalid translations.
> 
> Another alternative that just occurred to me would be to move
> the pci_address_to_pio() call from __of_address_to_resource()
> into of_bus_pci_translate() and then do the special handling
> for the ISA/LPC bus in of_bus_isa_translate().
> 
> 	Arnd

^ permalink raw reply

* Regression in next with "mfd: twl6040: The chip does not support bulk access"
From: Peter Ujfalusi @ 2016-09-23 10:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <139a8c7b-8cfe-fa21-634a-4551511f0cea@ti.com>

On 09/23/16 10:20, Peter Ujfalusi wrote:
> On 09/22/16 21:07, Tony Lindgren wrote:
>> Hi,
>>
>> Looks like commit 7a17e47f6403 ("mfd: twl6040: The chip does not
>> support bulk access") breaks at least omap4-duovero.
> 
> That's odd. I see no such errors on omap4 PandaBoard-ES nor on omap5 uEVM.
> The IRQ status is one register so even in bulk access it is one read. So the
> use_single_rw should have no effect on the access.
> 
> The only time when regmap would try to use bulk access to twl6040 is when we
> execute regcache_sync() on it after resuming the chip and this would fail at
> the first time when it tries to restore more than one consecutive registers.
> 
> I just tested things on next-20160915 and I see no errors at all.

Retested on next-20160923 and I see no issue on Panda-ES or omap5-uevm.

My guess is that you receive an interrupt from twl6040 while the chip is
powered down, the regmap is set to cache only. But this has nothing to do with
the bulk or non bulk access... Looking at the commit logs and git blame I see
no recent change in the driver(s) for twl6040 which changed the way it loads
and/or powers up/down.

-- 
P?ter

^ permalink raw reply

* [PATCH V6 3/5] PCI: thunder-pem: Allow to probe PEM-specific register range for ACPI case
From: Lorenzo Pieralisi @ 2016-09-23 10:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160922221042.GA1514@localhost>

[+ Zhang Rui]

On Thu, Sep 22, 2016 at 05:10:42PM -0500, Bjorn Helgaas wrote:
> On Thu, Sep 22, 2016 at 01:31:01PM -0500, Bjorn Helgaas wrote:
> > On Thu, Sep 22, 2016 at 01:44:46PM +0100, Lorenzo Pieralisi wrote:
> > > On Thu, Sep 22, 2016 at 11:10:13AM +0000, Gabriele Paoloni wrote:
> > > > Hi Lorenzo, Bjorn
> > > > 
> > > > > -----Original Message-----
> > > > > From: Lorenzo Pieralisi [mailto:lorenzo.pieralisi at arm.com]
> > > > > Sent: 22 September 2016 10:50
> > > > > To: Bjorn Helgaas
> > > > > Cc: Ard Biesheuvel; Tomasz Nowicki; David Daney; Will Deacon; Catalin
> > > > > Marinas; Rafael Wysocki; Arnd Bergmann; Hanjun Guo; Sinan Kaya;
> > > > > Jayachandran C; Christopher Covington; Duc Dang; Robert Richter; Marcin
> > > > > Wojtas; Liviu Dudau; Wangyijing; Mark Salter; linux-
> > > > > pci at vger.kernel.org; linux-arm-kernel at lists.infradead.org; Linaro ACPI
> > > > > Mailman List; Jon Masters; Andrea Gallo; Jeremy Linton; liudongdong
> > > > > (C); Gabriele Paoloni; Jeff Hugo; linux-acpi at vger.kernel.org; linux-
> > > > > kernel at vger.kernel.org; Rafael J. Wysocki
> > > > > Subject: Re: [PATCH V6 3/5] PCI: thunder-pem: Allow to probe PEM-
> > > > > specific register range for ACPI case
> > > > > 
> > > > > On Wed, Sep 21, 2016 at 01:04:57PM -0500, Bjorn Helgaas wrote:
> > > > > > On Wed, Sep 21, 2016 at 03:05:49PM +0100, Lorenzo Pieralisi wrote:
> > > > > > > On Tue, Sep 20, 2016 at 02:17:44PM -0500, Bjorn Helgaas wrote:
> > > > > > > > On Tue, Sep 20, 2016 at 04:09:25PM +0100, Ard Biesheuvel wrote:
> > > > > > >
> > > > > > > [...]
> > > > > > >
> > > > > > > > > None of these platforms can be fixed entirely in software, and
> > > > > given
> > > > > > > > > that we will not be adding quirks for new broken hardware, we
> > > > > should
> > > > > > > > > ask ourselves whether having two versions of a quirk, i.e., one
> > > > > for
> > > > > > > > > broken hardware + currently shipping firmware, and one for the
> > > > > same
> > > > > > > > > broken hardware with fixed firmware is really an improvement
> > > > > over what
> > > > > > > > > has been proposed here.
> > > > > > > >
> > > > > > > > We're talking about two completely different types of quirks:
> > > > > > > >
> > > > > > > >   1) MCFG quirks to use memory-mapped config space that doesn't
> > > > > quite
> > > > > > > >      conform to the ECAM model in the PCIe spec, and
> > > > > > > >
> > > > > > > >   2) Some yet-to-be-determined method to describe address space
> > > > > > > >      consumed by a bridge.
> > > > > > > >
> > > > > > > > The first two patches of this series are a nice implementation
> > > > > for 1).
> > > > > > > > The third patch (ThunderX-specific) is one possibility for 2),
> > > > > but I
> > > > > > > > don't like it because there's no way for generic software like
> > > > > the
> > > > > > > > ACPI core to discover these resources.
> > > > > > >
> > > > > > > Ok, so basically this means that to implement (2) we need to assign
> > > > > > > some sort of _HID to these quirky PCI bridges (so that we know what
> > > > > > > device they represent and we can retrieve their _CRS). I take from
> > > > > > > this discussion that the goal is to make sure that all non-config
> > > > > > > resources have to be declared through _CRS device objects, which is
> > > > > > > fine but that requires a FW update (unless we can fabricate ACPI
> > > > > > > devices and corresponding _CRS in the kernel whenever we match a
> > > > > > > given MCFG table signature).
> > > > > >
> > > > > > All resources consumed by ACPI devices should be declared through
> > > > > > _CRS.  If you want to fabricate ACPI devices or _CRS via kernel
> > > > > > quirks, that's fine with me.  This could be triggered via MCFG
> > > > > > signature, DMI info, host bridge _HID, etc.
> > > > > 
> > > > > I think the PNP quirk approach + PNP0c02 resource put forward by Gab
> > > > > is enough.
> > > > 
> > > > Great thanks as we take a final decision I will ask Dogndgong to submit
> > > > another RFC based on this approach
> > > > 
> > > > > 
> > > > > > > We discussed this already and I think we should make a decision:
> > > > > > >
> > > > > > > http://lists.infradead.org/pipermail/linux-arm-kernel/2016-
> > > > > March/414722.html
> > > > > > >
> > > > > > > > > > I'd like to step back and come up with some understanding of
> > > > > how
> > > > > > > > > > non-broken firmware *should* deal with this issue.  Then, if
> > > > > we *do*
> > > > > > > > > > work around this particular broken firmware in the kernel, it
> > > > > would be
> > > > > > > > > > nice to do it in a way that fits in with that understanding.
> > > > > > > > > >
> > > > > > > > > > For example, if a companion ACPI device is the preferred
> > > > > solution, an
> > > > > > > > > > ACPI quirk could fabricate a device with the required
> > > > > resources.  That
> > > > > > > > > > would address the problem closer to the source and make it
> > > > > more likely
> > > > > > > > > > that the rest of the system will work correctly: /proc/iomem
> > > > > could
> > > > > > > > > > make sense, things that look at _CRS generically would work
> > > > > (e.g,
> > > > > > > > > > /sys/, an admittedly hypothetical "lsacpi", etc.)
> > > > > > > > > >
> > > > > > > > > > Hard-coding stuff in drivers is a point solution that doesn't
> > > > > provide
> > > > > > > > > > any guidance for future platforms and makes it likely that
> > > > > the hack
> > > > > > > > > > will get copied into even more drivers.
> > > > > > > > > >
> > > > > > > > >
> > > > > > > > > OK, I see. But the guidance for future platforms should be 'do
> > > > > not
> > > > > > > > > rely on quirks', and what I am arguing here is that the more we
> > > > > polish
> > > > > > > > > up this code and make it clean and reusable, the more likely it
> > > > > is
> > > > > > > > > that will end up getting abused by new broken hardware that we
> > > > > set out
> > > > > > > > > to reject entirely in the first place.
> > > > > > > > >
> > > > > > > > > So of course, if the quirk involves claiming resources, let's
> > > > > make
> > > > > > > > > sure that this occurs in the cleanest and most compliant way
> > > > > possible.
> > > > > > > > > But any factoring/reuse concerns other than for the current
> > > > > crop of
> > > > > > > > > broken hardware should be avoided imo.
> > > > > > > >
> > > > > > > > If future hardware is completely ECAM-compliant and we don't need
> > > > > any
> > > > > > > > more MCFG quirks, that would be great.
> > > > > > >
> > > > > > > Yes.
> > > > > > >
> > > > > > > > But we'll still need to describe that memory-mapped config space
> > > > > > > > somewhere.  If that's done with PNP0C02 or similar devices (as is
> > > > > done
> > > > > > > > on my x86 laptop), we'd be all set.
> > > > > > >
> > > > > > > I am not sure I understand what you mean here. Are you referring
> > > > > > > to MCFG regions reported as PNP0c02 resources through its _CRS ?
> > > > > >
> > > > > > Yes.  PCI Firmware Spec r3.0, Table 4-2, note 2 says address ranges
> > > > > > reported via MCFG or _CBA should be reserved by _CRS of a PNP0C02
> > > > > > device.
> > > > > 
> > > > > Ok, that's agreed. It goes without saying that since you are quoting
> > > > > the PCI spec, if FW fails to report MCFG regions in a PNP0c02 device
> > > > > _CRS I will consider that a FW bug.
> > > > > 
> > > > > > > IIUC PNP0C02 is a reservation mechanism, but it does not help us
> > > > > > > associate its _CRS to a specific PCI host bridge instance, right ?
> > > > > >
> > > > > > Gab proposed a hierarchy that *would* associate a PNP0C02 device with
> > > > > > a PCI bridge:
> > > > > >
> > > > > >   Device (PCI1) {
> > > > > >     Name (_HID, "HISI0080") // PCI Express Root Bridge
> > > > > >     Name (_CID, "PNP0A03") // Compatible PCI Root Bridge
> > > > > >     Method (_CRS, 0, Serialized) { // Root complex resources
> > > > > (windows) }
> > > > > >     Device (RES0) {
> > > > > >       Name (_HID, "HISI0081") // HiSi PCIe RC config base address
> > > > > >       Name (_CID, "PNP0C02")  // Motherboard reserved resource
> > > > > >       Name (_CRS, ResourceTemplate () { ... }
> > > > > >     }
> > > > > >   }
> > > > > >
> > > > > > That's a possibility.  The PCI Firmware Spec suggests putting RES0 at
> > > > > > the root (under \_SB), but I don't know why.
> > > > > >
> > > > > > Putting it at the root means we couldn't generically associate it
> > > > > with
> > > > > > a bridge, although I could imagine something like this:
> > > > > >
> > > > > >   Device (RES1) {
> > > > > >     Name (_HID, "HISI0081") // HiSi PCIe RC config base address
> > > > > >     Name (_CID, "PNP0C02")  // Motherboard reserved resource
> > > > > >     Name (_CRS, ResourceTemplate () { ... }
> > > > > >     Method (BRDG) { "PCI1" }  // hand-wavy ASL
> > > > > >   }
> > > > > >   Device (PCI1) {
> > > > > >     Name (_HID, "HISI0080") // PCI Express Root Bridge
> > > > > >     Name (_CID, "PNP0A03") // Compatible PCI Root Bridge
> > > > > >     Method (_CRS, 0, Serialized) { // Root complex resources
> > > > > (windows) }
> > > > > >   }
> > > > > >
> > > > > > Where you could search PNP0C02 devices for a cookie that matched the
> > > > > > host bridge.o
> > > > > 
> > > > > Ok, I am fine with both and I think we are converging, but the way
> > > > > to solve this problem has to be uniform for all ARM partners (and
> > > > > not only ARM). Two points here:
> > > > > 
> > > > > 1) Adding a device/subdevice allows people to add a _CRS reporting the
> > > > >    non-window bridge resources. Fine. It also allows people to chuck in
> > > > >    there all sorts of _DSD properties to describe their PCI host bridge
> > > > >    as it is done with DT properties (those _DSD can contain eg clocks
> > > > >    etc.), this may be tempting (so that they can reuse the same DT
> > > > >    driver and do not have to update their firmware) but I want to be
> > > > >    clear here: that must not happen. So, a subdevice with a _CRS to
> > > > >    report resources, yes, but it will stop there.
> > > > > 2) It is unclear to me how to formalize the above. People should not
> > > > >    write FW by reading the PCI mailing list, so these guidelines have
> > > > > to
> > > > >    be written, somehow. I do not want to standardize quirks, I want
> > > > >    to prevent random ACPI table content, which is different.
> > > > >    Should I report this to the ACPI spec working group ? If we do
> > > > >    not do that everyone will go solve this problem as they deem fit.
> > > > > 
> > > > 
> > > > Do we really need to formalize this?
> > > > 
> > > > As we discussed in the Linaro call at the moment we have few vendors
> > > > that need quirks and we want to avoid promoting/accepting quirks for
> > > > the future.
> > > > 
> > > > At the time of the call I think we decided to informally accept a set
> > > > of quirks for the current platforms and reject any other quirk coming
> > > > after a certain date/kernel version (this to be decided).
> > > > 
> > > > I am not sure if there is a way to document/formalize a temporary
> > > > exception from the rule...
> > > 
> > > - (1) will be enforced.
> > 
> > I'm not sure it's necessary or possible to enforce a "no future
> > quirks" rule.  For one thing, there's already a pretty strong
> > incentive to avoid quirks: if your hardware doesn't require quirks,
> > it works with OSes already in the field.
> > 
> > MCFG quirks allow us to use the generic ACPI pci_root.c driver even if
> > the hardware doesn't support ECAM quite according to the spec.
> > 
> > PNP0C02 usage is a workaround for the failure of the Consumer/Producer
> > bit.  PNP0C02 quirks compensate for firmware that doesn't describe
> > resource usage accurately.  It's possible the ACPI spec folks could
> > come up with a better Consumer/Producer workaround, if that's needed.
> > Apparently x86 hasn't needed it yet.
> > 
> > If people add _DSD methods for clocks or whatnot, the hardware won't
> > work with the generic pci_root.c driver, so there's already an
> > incentive for avoiding them.  x86 has managed without such methods;
> > arm64 should be able to do the same.
> 
> Re-reading this, I'm afraid my response sounds a little dismissive,
> and I feel like I'm missing some important information.  So I
> apologize if I missed your whole point, Lorenzo.

No you are spot on, I just wanted to emphasize, given that we are
adding an _HID and a subdevice, that developer should not be tempted
to use it to match against a PCI host driver to reuse the DT code,
we should not use the quirk mechanism as a backdoor to re-using DT
drivers in ACPI context.

Anyway, there is a review process to spot these possible misuses,
mine was just a heads-up, quirks will happen, I just do not want
to wreak the standard ACPI PCI firmware model to support them.

Given that there are already PNP0c02 bindings out there where the
PNP0c02 is used as in Gab's example:

https://patchwork.kernel.org/patch/4757111/

I think the only pending question I have is whether we are allowed
to define a PNP0A03 subdevice with a _CRS resource space that is
not contained in its parent _CRS, if we answer this question I
think we are done.

I will raise the PNP0c02 usage issue with the ASWG anyway.

Thanks !
Lorenzo

^ permalink raw reply

* [PATCH v2] gpio: Added zynq specific check for special pins on bank zero
From: Nava kishore Manne @ 2016-09-23 10:05 UTC (permalink / raw)
  To: linux-arm-kernel

From: Nava kishore Manne <nava.manne@xilinx.com>

This patch adds zynq specific check for bank 0 pins 7 and 8
are special and cannot be used as inputs

Signed-off-by: Nava kishore Manne <navam@xilinx.com>
Acked-by: Michal Simek <michal.simek@xilinx.com>
---
Changes for v2:
                -Removed un-used quirks for zynqmp.

 drivers/gpio/gpio-zynq.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index e72794e..2b689e8 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -96,6 +96,9 @@
 /* GPIO upper 16 bit mask */
 #define ZYNQ_GPIO_UPPER_MASK 0xFFFF0000
 
+/* For GPIO quirks */
+#define ZYNQ_GPIO	BIT(0)
+
 /**
  * struct zynq_gpio - gpio device private data structure
  * @chip:	instance of the gpio_chip
@@ -122,6 +125,7 @@ struct zynq_gpio {
 */
 struct zynq_platform_data {
 	const char *label;
+	u32 quirks;
 	u16 ngpio;
 	int max_bank;
 	int bank_min[ZYNQMP_GPIO_MAX_BANK];
@@ -238,13 +242,19 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
 static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
 {
 	u32 reg;
+	bool is_zynq_gpio;
 	unsigned int bank_num, bank_pin_num;
 	struct zynq_gpio *gpio = gpiochip_get_data(chip);
 
+	is_zynq_gpio = gpio->p_data->quirks & ZYNQ_GPIO;
 	zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
 
-	/* bank 0 pins 7 and 8 are special and cannot be used as inputs */
-	if (bank_num == 0 && (bank_pin_num == 7 || bank_pin_num == 8))
+	/*
+	 * On zynq bank 0 pins 7 and 8 are special and cannot be used
+	 * as inputs.
+	 */
+	if (is_zynq_gpio && bank_num == 0 &&
+		(bank_pin_num == 7 || bank_pin_num == 8))
 		return -EINVAL;
 
 	/* clear the bit in direction mode reg to set the pin as input */
@@ -627,6 +637,7 @@ static const struct zynq_platform_data zynqmp_gpio_def = {
 
 static const struct zynq_platform_data zynq_gpio_def = {
 	.label = "zynq_gpio",
+	.quirks = ZYNQ_GPIO,
 	.ngpio = ZYNQ_GPIO_NR_GPIOS,
 	.max_bank = ZYNQ_GPIO_MAX_BANK,
 	.bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(),
-- 
2.1.2

^ permalink raw reply related

* [PATCH v2 2/3] arm64: kgdb: prevent kgdb from being invoked recursively
From: Will Deacon @ 2016-09-23 10:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160923073327.9657-3-takahiro.akashi@linaro.org>

On Fri, Sep 23, 2016 at 04:33:26PM +0900, AKASHI Takahiro wrote:
> If a breakpoint is set in an interrupt-sensitive place,
> like gic_handle_irq(), a debug exception can be triggerred recursively.
> We will see the following message:
> 
>     KGDB: re-enter error: breakpoint removed ffffffc000081258
>     ------------[ cut here ]------------
>     WARNING: CPU: 0 PID: 650 at kernel/debug/debug_core.c:435
>                                         kgdb_handle_exception+0x1dc/0x1f4()
>     Modules linked in:
>     CPU: 0 PID: 650 Comm: sh Not tainted 3.17.0-rc2+ #177
>     Call trace:
>     [<ffffffc000087fac>] dump_backtrace+0x0/0x130
>     [<ffffffc0000880ec>] show_stack+0x10/0x1c
>     [<ffffffc0004d683c>] dump_stack+0x74/0xb8
>     [<ffffffc0000ab824>] warn_slowpath_common+0x8c/0xb4
>     [<ffffffc0000ab90c>] warn_slowpath_null+0x14/0x20
>     [<ffffffc000121bfc>] kgdb_handle_exception+0x1d8/0x1f4
>     [<ffffffc000092ffc>] kgdb_brk_fn+0x18/0x28
>     [<ffffffc0000821c8>] brk_handler+0x9c/0xe8
>     [<ffffffc0000811e8>] do_debug_exception+0x3c/0xac
>     Exception stack(0xffffffc07e027650 to 0xffffffc07e027770)
>     ...
>     [<ffffffc000083cac>] el1_dbg+0x14/0x68
>     [<ffffffc00012178c>] kgdb_cpu_enter+0x464/0x5c0
>     [<ffffffc000121bb4>] kgdb_handle_exception+0x190/0x1f4
>     [<ffffffc000092ffc>] kgdb_brk_fn+0x18/0x28
>     [<ffffffc0000821c8>] brk_handler+0x9c/0xe8
>     [<ffffffc0000811e8>] do_debug_exception+0x3c/0xac
>     Exception stack(0xffffffc07e027ac0 to 0xffffffc07e027be0)
>     ...
>     [<ffffffc000083cac>] el1_dbg+0x14/0x68
>     [<ffffffc00032e4b4>] __handle_sysrq+0x11c/0x190
>     [<ffffffc00032e93c>] write_sysrq_trigger+0x4c/0x60
>     [<ffffffc0001e7d58>] proc_reg_write+0x54/0x84
>     [<ffffffc000192fa4>] vfs_write+0x98/0x1c8
>     [<ffffffc0001939b0>] SyS_write+0x40/0xa0
> 
> When some interrupt occurs, a breakpoint at gic_handle_irq() invokes kgdb.
> Kgdb then calls kgdb_roundup_cpus() to sync with other cpus. Current
> kgdb_roundup_cpus() unmasks interrupts temporarily to use
> smp_call_function(). This eventually allows another interrupt to occur and
> likely results in hitting a breakpoint at gic_handle_irq() again since
> a debug exception is always enabled in el1_irq.
> 
> We can avoid this issue by specifying "nokgdbroundup" in a kernel command
> line, but this will also leave other cpus be in unknown state in terms of
> kgdb, and may result in interfering with kgdb.
> 
> This patch re-implements kgdb_roundup_cpus() so that we won't have to
> enable interrupts there by using irq_work.
> 
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: Jason Wessel <jason.wessel@windriver.com>
> Cc: <stable@vger.kernel.org> # 3.15-
> ---
>  arch/arm64/kernel/kgdb.c | 32 ++++++++++++++++++++++++++++----
>  1 file changed, 28 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c
> index afe5f90..59c4aec 100644
> --- a/arch/arm64/kernel/kgdb.c
> +++ b/arch/arm64/kernel/kgdb.c
> @@ -19,10 +19,13 @@
>   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
>   */
>  
> +#include <linux/cpumask.h>
>  #include <linux/irq.h>
> +#include <linux/irq_work.h>
>  #include <linux/kdebug.h>
>  #include <linux/kgdb.h>
>  #include <linux/kprobes.h>
> +#include <linux/percpu.h>
>  #include <asm/traps.h>
>  
>  struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
> @@ -106,6 +109,8 @@ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
>  	{ "fpcr", 4, -1 },
>  };
>  
> +static DEFINE_PER_CPU(struct irq_work, kgdb_irq_work);
> +
>  char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
>  {
>  	if (regno >= DBG_MAX_REG_NUM || regno < 0)
> @@ -258,16 +263,27 @@ static struct step_hook kgdb_step_hook = {
>  	.fn		= kgdb_step_brk_fn
>  };
>  
> -static void kgdb_call_nmi_hook(void *ignored)
> +static void kgdb_roundup_hook(struct irq_work *work)
>  {
>  	kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs());
>  }
>  
>  void kgdb_roundup_cpus(unsigned long flags)
>  {
> -	local_irq_enable();
> -	smp_call_function(kgdb_call_nmi_hook, NULL, 0);
> -	local_irq_disable();
> +	int cpu;
> +	struct cpumask mask;
> +	struct irq_work *work;
> +
> +	mask = *cpu_online_mask;

Are you sure you want to do this on the stack? Assuming it's safe to
allocate here, why not use cpumask_var_t?

Will

^ permalink raw reply

* [PATCH V3 2/4] ARM64 LPC: LPC driver implementation on Hip06
From: Arnd Bergmann @ 2016-09-23  9:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <57E40665.8080005@gmail.com>

On Friday, September 23, 2016 12:27:17 AM CEST zhichang.yuan wrote:
> For this patch sketch, I have a question.
> Do we call pci_address_to_pio in arch_of_address_to_pio to get the 
> corresponding logical IO port
> for LPC??


No, of course not, that would be silly:

The argument to pci_address_to_pio() is a phys_addr_t, and we we don't
have one because there is no address associated with your PIO, that
is the entire point of your driver!

Also, we already know the mapping because this is what the inb/outb
workaround is looking at, so there is absolutely no reason to call it
either.

> If we don't, it seems the LPC specific IO address will conflict with PCI 
> host bridges' logical IO.
>
> Supposed our LPC populated the IO range from 0x100 to 0x3FF( this is 
> normal for ISA similar
> devices), after arch_of_address_to_pio(), the r->start will be set as 
> 0x100, r->end will be set as
> 0x3FF.  And if there is one PCI host bridge who request a IO window size 
> over 0x400 at the same
> time, the  corresponding r->start and r->end will be set as 0x0, 0x3FF 
> after of_address_to_resource
> for this host bridge.  Then the IO conflict happens.

You would still need to reserve some space in the io_range_list
to avoid possible conflicts, which is a bit ugly with the current
definition of pci_register_io_range, but I'm sure can be done.

One way I can think of would be to change pci_register_io_range()
to just return the logical port number directly (it already
knows it!), and pass an invalid physical address (e.g. 
#define ISA_WORKAROUND_IO_PORT_WINDOW -0x10000) into it for
invalid translations.

Another alternative that just occurred to me would be to move
the pci_address_to_pio() call from __of_address_to_resource()
into of_bus_pci_translate() and then do the special handling
for the ISA/LPC bus in of_bus_isa_translate().

	Arnd

^ permalink raw reply

* [PATCH v6 1/7] arm/arm64: vgic-new: Implement support for userspace access
From: Marc Zyngier @ 2016-09-23  9:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CALicx6uuL9xX3VxLCy+EZi0AqXrNM_O+F3MN+W4gbiriUyb7Ug@mail.gmail.com>

On 22/09/16 15:01, Vijay Kilari wrote:
> On Thu, Sep 22, 2016 at 5:38 PM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> On 20/09/16 07:12, vijay.kilari at gmail.com wrote:
>>> From: Vijaya Kumar K <Vijaya.Kumar@cavium.com>
>>> +static int vgic_uaccess_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
>>> +                          gpa_t addr, u32 *val)
>>> +{
>>> +     struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev);
>>> +     const struct vgic_register_region *region;
>>> +     struct kvm_vcpu *r_vcpu;
>>> +
>>> +     region = vgic_get_mmio_region(iodev, addr, sizeof(u32));
>>> +     if (!region) {
>>> +             *val = 0;
>>> +             return 0;
>>
>> This is not the previous semantic of vgic_uaccess, and I cannot see why
>> blindly ignoring an access to an undefined region would be acceptable.
>> What am I missing?
> 
> AFAIK, the vgic_uaccess is not making any check on undefined region/register.
> However, dispatch_mmio_read/write are returning 0 if check of region is failed

Hmmm. Fair enough. I don't really like it, but that's something for
another day.

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply

* [PATCH v2.1] arm64: kgdb: handle read-only text / modules
From: Catalin Marinas @ 2016-09-23  9:49 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160923074208.9899-1-takahiro.akashi@linaro.org>

On Fri, Sep 23, 2016 at 04:42:08PM +0900, AKASHI Takahiro wrote:
> Handle read-only cases (CONFIG_DEBUG_RODATA/CONFIG_DEBUG_SET_MODULE_RONX)
> by using aarch64_insn_write() instead of probe_kernel_write().
> See how this works:
>     commit 2f896d586610 ("arm64: use fixmap for text patching")
> 
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> Reviewed-by: Mark Rutland <mark.rutland@arm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: Jason Wessel <jason.wessel@windriver.com>
> Cc: <stable@vger.kernel.org> # 3.18-3.19: 2f896d5: arm64: use fixmap
> Cc: <stable@vger.kernel.org> # 3.18-3.19: f6242ca: arm64: Fix text
> Cc: <stable@vger.kernel.org> # 4.0-

Queued for 4.8 with a slight change in the last Cc: tag above:

Cc: <stable@vger.kernel.org> # 3.18.x-

Thanks.

-- 
Catalin

^ permalink raw reply

* [PATCH v3] arm: dts: zynq: Add MicroZed board support
From: Jagan Teki @ 2016-09-23  9:48 UTC (permalink / raw)
  To: linux-arm-kernel

From: Jagan Teki <jteki@openedev.com>

Added basic dts support for MicroZed board.

- UART
- SDHCI
- Ethernet

Cc: Soren Brinkmann <soren.brinkmann@xilinx.com>
Cc: Michal Simek <michal.simek@xilinx.com>
Signed-off-by: Jagan Teki <jteki@openedev.com>
---
Changes for v3:
	- Add Xilinx copyright
Changes for v2:
	- Add SDHCI
	- Add Ethernet

 arch/arm/boot/dts/Makefile          |  1 +
 arch/arm/boot/dts/zynq-microzed.dts | 96 +++++++++++++++++++++++++++++++++++++
 2 files changed, 97 insertions(+)
 create mode 100644 arch/arm/boot/dts/zynq-microzed.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index faacd52..4d7b858 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -862,6 +862,7 @@ dtb-$(CONFIG_ARCH_VT8500) += \
 	wm8750-apc8750.dtb \
 	wm8850-w70v2.dtb
 dtb-$(CONFIG_ARCH_ZYNQ) += \
+	zynq-microzed.dtb \
 	zynq-parallella.dtb \
 	zynq-zc702.dtb \
 	zynq-zc706.dtb \
diff --git a/arch/arm/boot/dts/zynq-microzed.dts b/arch/arm/boot/dts/zynq-microzed.dts
new file mode 100644
index 0000000..b9376a4
--- /dev/null
+++ b/arch/arm/boot/dts/zynq-microzed.dts
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011 - 2014 Xilinx
+ * Copyright (C) 2016 Jagan Teki <jteki@openedev.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+/dts-v1/;
+/include/ "zynq-7000.dtsi"
+
+/ {
+	model = "Zynq MicroZED Development Board";
+	compatible = "xlnx,zynq-microzed", "xlnx,zynq-7000";
+
+	aliases {
+		ethernet0 = &gem0;
+		serial0 = &uart1;
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x0 0x40000000>;
+	};
+
+	chosen {
+		bootargs = "earlycon";
+		stdout-path = "serial0:115200n8";
+	};
+
+	usb_phy0: phy0 {
+		compatible = "usb-nop-xceiv";
+		#phy-cells = <0>;
+	};
+};
+
+&clkc {
+	ps-clk-frequency = <33333333>;
+};
+
+&gem0 {
+	status = "okay";
+	phy-mode = "rgmii-id";
+	phy-handle = <&ethernet_phy>;
+
+	ethernet_phy: ethernet-phy at 0 {
+		reg = <0>;
+	};
+};
+
+&sdhci0 {
+	status = "okay";
+};
+
+&uart1 {
+	status = "okay";
+};
+
+&usb0 {
+	status = "okay";
+	dr_mode = "host";
+	usb-phy = <&usb_phy0>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usb0_default>;
+};
+
+&pinctrl0 {
+	pinctrl_usb0_default: usb0-default {
+		mux {
+			groups = "usb0_0_grp";
+			function = "usb0";
+		};
+
+		conf {
+			groups = "usb0_0_grp";
+			slew-rate = <0>;
+			io-standard = <1>;
+		};
+
+		conf-rx {
+			pins = "MIO29", "MIO31", "MIO36";
+			bias-high-impedance;
+		};
+
+		conf-tx {
+			pins = "MIO28", "MIO30", "MIO32", "MIO33", "MIO34",
+			       "MIO35", "MIO37", "MIO38", "MIO39";
+			bias-disable;
+		};
+	};
+};
-- 
2.7.4

^ permalink raw reply related

* [PATCH 00/24] ste_dma40: Fine-tuning for several function implementations
From: Linus Walleij @ 2016-09-23  9:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <92810066-69b6-94e7-dcec-a28594b1328f@users.sourceforge.net>

On Sat, Sep 17, 2016 at 5:05 PM, SF Markus Elfring
<elfring@users.sourceforge.net> wrote:

> From: Markus Elfring <elfring@users.sourceforge.net>
> Date: Sat, 17 Sep 2016 16:56:56 +0200
>
> Several update suggestions were taken into account
> from static source code analysis.

All look very nice!

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>

Vinod, please apply them all!

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH] arm/arm64: KVM: Add support for ARMv8 AArch32 execution state
From: Marc Zyngier @ 2016-09-23  9:30 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160923092550.GG9101@cbox>

On 23/09/16 10:25, Christoffer Dall wrote:
> On Fri, Sep 23, 2016 at 03:13:25PM +0800, Alison Wang wrote:
>> The ARMv8 architecture supports two execution state, AArch64 and
>> AArch32. To support KVM in AArch32 execution state for ARMv8, Cortex-A53
>> and Cortex-A72 need to be added for target-specific checks.
>>
>> Signed-off-by: Alison Wang <alison.wang@nxp.com>
>> ---
>>  arch/arm/include/asm/cputype.h | 2 ++
>>  arch/arm/kvm/guest.c           | 2 ++
>>  2 files changed, 4 insertions(+)
>>
>> diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h
>> index 754f86f..4f8c632 100644
>> --- a/arch/arm/include/asm/cputype.h
>> +++ b/arch/arm/include/asm/cputype.h
>> @@ -75,6 +75,8 @@
>>  #define ARM_CPU_PART_CORTEX_A12		0x4100c0d0
>>  #define ARM_CPU_PART_CORTEX_A17		0x4100c0e0
>>  #define ARM_CPU_PART_CORTEX_A15		0x4100c0f0
>> +#define ARM_CPU_PART_CORTEX_A53_AARCH32	0x4100d030
>> +#define ARM_CPU_PART_CORTEX_A72_AARCH32	0x4100d080
>>  #define ARM_CPU_PART_MASK		0xff00fff0
>>  
>>  /* DEC implemented cores */
>> diff --git a/arch/arm/kvm/guest.c b/arch/arm/kvm/guest.c
>> index 9aca920..462a099 100644
>> --- a/arch/arm/kvm/guest.c
>> +++ b/arch/arm/kvm/guest.c
>> @@ -252,6 +252,8 @@ int __attribute_const__ kvm_target_cpu(void)
>>  {
>>  	switch (read_cpuid_part()) {
>>  	case ARM_CPU_PART_CORTEX_A7:
>> +	case ARM_CPU_PART_CORTEX_A53_AARCH32:
>> +	case ARM_CPU_PART_CORTEX_A72_AARCH32:
> 
> huh?  why are we mapping A53 and A72 cores to an A7 core?

Yeah, that's absolutely disgusting. The only sensible thing to do would
be to implement something "generic", just like we have on the 64bit side.

As it stands, this patch falls into the "No way" bucket.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox