Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v9 06/11] arm/arm64: vgic: Implement VGICv3 CPU interface access
From: Christoffer Dall @ 2016-11-29  8:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CALicx6s4E0-VJMm6Ff5qu=sZgXu-mUnJdDCmaiZO0i3yzF8wxA@mail.gmail.com>

On Tue, Nov 29, 2016 at 01:08:26PM +0530, Vijay Kilari wrote:
> On Tue, Nov 29, 2016 at 1:09 AM, Christoffer Dall
> <christoffer.dall@linaro.org> wrote:
> > On Wed, Nov 23, 2016 at 06:31:53PM +0530, vijay.kilari at gmail.com wrote:
> >> From: Vijaya Kumar K <Vijaya.Kumar@cavium.com>
> >>
> >> VGICv3 CPU interface registers are accessed using
> >> KVM_DEV_ARM_VGIC_CPU_SYSREGS ioctl. These registers are accessed
> >> as 64-bit. The cpu MPIDR value is passed along with register id.
> >> is used to identify the cpu for registers access.
> >>
> >> The VM that supports SEIs expect it on destination machine to handle
> >> guest aborts and hence checked for ICC_CTLR_EL1.SEIS compatibility.
> >> Similarly, VM that supports Affinity Level 3 that is required for AArch64
> >> mode, is required to be supported on destination machine. Hence checked
> >> for ICC_CTLR_EL1.A3V compatibility.
> >>
> >> The CPU system register handling is spitted into two files
> >
> > spitted?  Did you mean 'split into' ?
> >
> >> vgic-sys-reg-common.c and vgic-sys-reg-v3.c.
> >> The vgic-sys-reg-common.c handles read and write of VGIC CPU registers
> >
> > So this is weird because everything in virt/kvm/arm/ is exactly supposed
> > to be common between arm and arm64 already.
> >
> > I would rather that you had a copy of vgic-sys-reg-v3.c in arch/arm/kvm/
> > and in arch/arm64/kvm/ each taking care of its own architecture.
> >
> > But note that I didn't actually require that you implemented support for
> > GICv3 migration on AArch32 hosts for these patches, I just didn't want
> > thigns to silently break.
> >
> > If we cannot test the AArch32 implementation, we should potentially just
> > make sure that is not supported yet, return a proper error to userspace
> > and get the AArch64 host implementation correct.
> >
> > I suggest you move your:
> >   virt/kvm/arm/vgic/vgic-sys-reg-v3.c to
> >   arch/arm64/kvm/vgic-sys-reg-v3.c
> >
> > and rename
> >   virt/kvm/arm/vgic/vgic-sys-reg-common.c to
> >   virt/kvm/arm/vgic/vgic-sys-reg-v3.c
> >
> > And then wait with the AArch32 host side for now, but just make sure it
> > compiles and returns an error as opposed to crashing the system if
> > someone tries to excercise this interface on an AArch32 host.
> 
> I will add arch/arm/kvm/vgic-coproc-v3.c (pls check if file name is ok or not?)

I would call it vgic-v3-coproc.c

> and return -ENXIO as shown below and update document accordingly.
> 
> int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, bool is_write, u64 id,
>                                u64 *reg)
> {
>        /*
>         * TODO: Implement for AArch32
>         */
>        return -ENXIO;
> }
> 
> int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, bool is_write, u64 id,
>                                u64 *reg)
> {
>        /*
>         * TODO: Implement for AArch32
>         */
>        return -ENXIO;
> }


> 
> >
> >> for both AArch64 and AArch32 mode. The vgic-sys-reg-v3.c handles AArch64
> >> mode and is compiled only for AArch64 mode.
> >>
> >> Updated arch/arm/include/uapi/asm/kvm.h with new definitions
> >> required to compile for AArch32.
> >>
> >> The version of VGIC v3 specification is define here
> >> Documentation/virtual/kvm/devices/arm-vgic-v3.txt
> >>
> >> Signed-off-by: Pavel Fedin <p.fedin@samsung.com>
> >> Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@cavium.com>
> >> ---
> [...]
> >> +static bool access_gic_aprn(struct kvm_vcpu *vcpu, bool is_write, u8 apr,
> >> +                         u8 idx, unsigned long *reg)
> >> +{
> >> +     struct vgic_cpu *vgic_v3_cpu = &vcpu->arch.vgic_cpu;
> >> +
> >> +     /* num_pri_bits are initialized with HW supported values.
> >> +      * We can rely safely on num_pri_bits even if VM has not
> >> +      * restored ICC_CTLR_EL1 before restoring APnR registers.
> >> +      */
> >
> > nit: commenting style
> ok
> >
> >> +     switch (vgic_v3_cpu->num_pri_bits) {
> >> +     case 7:
> >> +             vgic_v3_access_apr_reg(vcpu, is_write, apr, idx, reg);
> >> +             break;
> >> +     case 6:
> >> +             if (idx > 1)
> >> +                     goto err;
> >> +             vgic_v3_access_apr_reg(vcpu, is_write, apr, idx, reg);
> >> +             break;
> >> +     default:
> >> +             if (idx > 0)
> >> +                     goto err;
> >> +             vgic_v3_access_apr_reg(vcpu, is_write, apr, idx, reg);
> >> +     }
> >
> > It looks to me like userspace can then program active priorities with
> > higher numbers than what it will program num_pri_bits to later.  Is that
> > not weird, or am I missing something?
> 
> As long as it is within HW supported priorities it is safe.

I know that it is safe on the hardware, but it is weird to define a VM
with some max priority and still be able to set a higher active priority
is it not?

On the other hand, if we cannot enforce this at runtime, it may not
matter?

Hint: I'd like for you to actually think about these constraints and
make sure the sematics of the emulated VM environment remain intact
across migrations.

> >
> >> +
> >> +     return true;
> >> +err:
> >> +     if (!is_write)
> >> +             *reg = 0;
> >> +
> >> +     return false;
> >> +}
> >> +
> >> +bool access_gic_ap0r_reg(struct kvm_vcpu *vcpu, bool is_write, u8 idx,
> >> +                      unsigned long *reg)
> >> +{
> >> +     return access_gic_aprn(vcpu, is_write, 0, idx, reg);
> >> +}
> >> +
> >> +bool access_gic_ap1r_reg(struct kvm_vcpu *vcpu, bool is_write, u8 idx,
> >> +                      unsigned long *reg)
> >> +{
> >> +     return access_gic_aprn(vcpu, is_write, 1, idx, reg);
> >> +}
> >> +
> >> +bool access_gic_sre_reg(struct kvm_vcpu *vcpu, bool is_write,
> >> +                     unsigned long *reg)
> >> +{
> >> +     struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3;
> >> +
> >> +     /* Validate SRE bit */
> >> +     if (is_write) {
> >> +             if (!(*reg & ICC_SRE_EL1_SRE))
> >> +                     return false;
> >> +     } else {
> >> +             *reg = vgicv3->vgic_sre;
> >> +     }
> >> +
> >> +     return true;
> >> +}
> >> diff --git a/virt/kvm/arm/vgic/vgic-sys-reg-v3.c b/virt/kvm/arm/vgic/vgic-sys-reg-v3.c
> >> new file mode 100644
> >> index 0000000..82c2f02
> >> --- /dev/null
> >> +++ b/virt/kvm/arm/vgic/vgic-sys-reg-v3.c
> >> @@ -0,0 +1,142 @@
> >> +/*
> >> + * VGIC system registers handling functions
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License version 2 as
> >> + * published by the Free Software Foundation.
> >> + *
> >> + * This program is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >> + * GNU General Public License for more details.
> >> + */
> >> +
> >> +#include <linux/kvm.h>
> >> +#include <linux/kvm_host.h>
> >> +#include <asm/kvm_emulate.h>
> >> +#include "vgic.h"
> >> +#include "sys_regs.h"
> >> +
> >> +#define ACCESS_SYS_REG(REG)                                          \
> >> +static bool access_gic_##REG##_sys_reg(struct kvm_vcpu *vcpu,                \
> >> +                                 struct sys_reg_params *p,           \
> >> +                                 const struct sys_reg_desc *r)       \
> >> +{                                                                    \
> >> +     unsigned long tmp;                                              \
> >> +     bool ret;                                                       \
> >> +                                                                     \
> >> +     if (p->is_write)                                                \
> >> +             tmp = p->regval;                                        \
> >> +     ret = access_gic_##REG##_reg(vcpu, p->is_write, &tmp);          \
> >> +     if (!p->is_write)                                               \
> >> +             p->regval = tmp;                                        \
> >> +                                                                     \
> >> +     return ret;                                                     \
> >> +}
> >> +
> >> +ACCESS_SYS_REG(ctlr)
> >> +ACCESS_SYS_REG(pmr)
> >> +ACCESS_SYS_REG(bpr0)
> >> +ACCESS_SYS_REG(bpr1)
> >> +ACCESS_SYS_REG(sre)
> >> +ACCESS_SYS_REG(grpen0)
> >> +ACCESS_SYS_REG(grpen1)
> >> +
> >> +#define ACCESS_APNR_SYS_REG(REG)                                     \
> >> +static bool access_gic_##REG##_sys_reg(struct kvm_vcpu *vcpu,                \
> >> +                                 struct sys_reg_params *p,           \
> >> +                                 const struct sys_reg_desc *r)       \
> >> +{                                                                    \
> >> +     unsigned long tmp;                                              \
> >> +     u8 idx = p->Op2 & 3;                                            \
> >> +     bool ret;                                                       \
> >> +                                                                     \
> >> +     if (p->is_write)                                                \
> >> +             tmp = p->regval;                                        \
> >> +     ret = access_gic_##REG##_reg(vcpu, p->is_write, idx, &tmp);     \
> >> +     if (!p->is_write)                                               \
> >> +             p->regval = tmp;                                        \
> >> +                                                                     \
> >> +     return ret;                                                     \
> >> +}
> >> +
> >> +ACCESS_APNR_SYS_REG(ap0r)
> >> +ACCESS_APNR_SYS_REG(ap1r)
> >
> > I don't get these indirections.  Why can't you call the functions
> > directly?
> 
> The code is same for accessing the registers hence added this indirection.
> 

That's not answering my question.

What is the benefit of adding this indirection as opposed to having the
functions called directly?

To make my point clear:  I hate this kind of preprocessor macro fun, and
I think it should only ever be used when there's a huge benefit in terms
of code reuse or simplicity of some sort.  I don't see anything like
that in this case.

Thanks,
-Christoffer

^ permalink raw reply

* [PATCH v7 3/8] drm: sun8i: add HDMI video support to A83T and H3
From: Jean-Francois Moine @ 2016-11-29  8:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1480414715.git.moinejf@free.fr>

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 drivers/gpu/drm/sun8i/Kconfig       |   7 +
 drivers/gpu/drm/sun8i/Makefile      |   2 +
 drivers/gpu/drm/sun8i/de2_hdmi.c    | 440 +++++++++++++++++++
 drivers/gpu/drm/sun8i/de2_hdmi.h    |  51 +++
 drivers/gpu/drm/sun8i/de2_hdmi_io.c | 843 ++++++++++++++++++++++++++++++++++++
 5 files changed, 1343 insertions(+)
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.c
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.h
 create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi_io.c

diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
index 6940895..5c4607b 100644
--- a/drivers/gpu/drm/sun8i/Kconfig
+++ b/drivers/gpu/drm/sun8i/Kconfig
@@ -17,3 +17,10 @@ config DRM_SUN8I_DE2
 	  Choose this option if your Allwinner chipset has the DE2 interface
 	  as the A64, A83T and H3. If M is selected the module will be called
 	  sun8i-de2-drm.
+
+config DRM_SUN8I_DE2_HDMI
+	tristate "Support for DE2 HDMI"
+	depends on DRM_SUN8I_DE2
+	help
+	  Choose this option if you use want HDMI on DE2.
+	  If M is selected the module will be called sun8i-de2-hdmi.
diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
index f107919..6ba97c2 100644
--- a/drivers/gpu/drm/sun8i/Makefile
+++ b/drivers/gpu/drm/sun8i/Makefile
@@ -3,5 +3,7 @@
 #
 
 sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
+sun8i-de2-hdmi-objs := de2_hdmi.o de2_hdmi_io.o
 
 obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
+obj-$(CONFIG_DRM_SUN8I_DE2_HDMI) += sun8i-de2-hdmi.o
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi.c b/drivers/gpu/drm/sun8i/de2_hdmi.c
new file mode 100644
index 0000000..9ff6132
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi.c
@@ -0,0 +1,440 @@
+/*
+ * Allwinner DRM driver - HDMI
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/hdmi.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include "de2_hdmi.h"
+
+static const struct of_device_id de2_hdmi_dt_ids[] = {
+	{ .compatible = "allwinner,sun8i-a83t-hdmi",
+					.data = (void *) SOC_A83T },
+	{ .compatible = "allwinner,sun8i-h3-hdmi",
+					.data = (void *) SOC_H3 },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, de2_hdmi_dt_ids);
+
+#define conn_to_priv(x) \
+	container_of(x, struct de2_hdmi_priv, connector)
+
+#define enc_to_priv(x) \
+	container_of(x, struct de2_hdmi_priv, encoder)
+
+/* --- encoder functions --- */
+
+static int de2_hdmi_set_clock(struct de2_hdmi_priv *priv,
+				int rate)
+{
+	struct clk *parent_clk;
+	u32 parent_rate;
+	int ret;
+
+	/* determine and set the best rate for the parent clock (pll-video) */
+	if ((270000 * 2) % rate == 0)
+		parent_rate = 270000000;
+	else if (297000 % rate == 0)
+		parent_rate = 297000000;
+	else
+		return -EINVAL;			/* unsupported clock */
+
+	parent_clk = clk_get_parent(priv->clk);
+
+	ret = clk_set_rate(parent_clk, parent_rate);
+	if (ret) {
+		dev_err(priv->dev, "set parent rate failed %d\n", ret);
+		return ret;
+	}
+	ret = clk_set_rate(priv->clk, rate * 1000);
+	if (ret)
+		dev_err(priv->dev, "set rate failed %d\n", ret);
+
+	return ret;
+}
+
+static void de2_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+				      struct drm_display_mode *mode,
+				      struct drm_display_mode *adjusted_mode)
+{
+	struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+	priv->cea_mode = drm_match_cea_mode(mode);
+
+	DRM_DEBUG_DRIVER("cea_mode %d\n", priv->cea_mode);
+
+	if (de2_hdmi_set_clock(priv, mode->clock) < 0)
+		return;
+
+	mutex_lock(&priv->mutex);
+	hdmi_io_mode_set(priv, mode);
+	mutex_unlock(&priv->mutex);
+}
+
+static void de2_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+	mutex_lock(&priv->mutex);
+	hdmi_io_video_on(priv);
+	mutex_unlock(&priv->mutex);
+}
+
+static void de2_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+	mutex_lock(&priv->mutex);
+	hdmi_io_video_off(priv);
+	mutex_unlock(&priv->mutex);
+}
+
+static const struct drm_encoder_helper_funcs de2_hdmi_encoder_helper_funcs = {
+	.mode_set = de2_hdmi_encoder_mode_set,
+	.enable = de2_hdmi_encoder_enable,
+	.disable = de2_hdmi_encoder_disable,
+};
+
+static const struct drm_encoder_funcs de2_hdmi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+/* --- connector functions --- */
+
+static int de2_hdmi_connector_mode_valid(struct drm_connector *connector,
+					struct drm_display_mode *mode)
+{
+	int cea_mode = drm_match_cea_mode(mode);
+
+	return hdmi_io_mode_valid(cea_mode) < 0 ? MODE_NOMODE : MODE_OK;
+}
+
+static enum drm_connector_status de2_hdmi_connector_detect(
+				struct drm_connector *connector, bool force)
+{
+	struct de2_hdmi_priv *priv = conn_to_priv(connector);
+	int ret;
+
+	mutex_lock(&priv->mutex);
+	ret = hdmi_io_get_hpd(priv);
+	mutex_unlock(&priv->mutex);
+
+	return ret ? connector_status_connected :
+			connector_status_disconnected;
+}
+
+static int read_edid_block(void *data, u8 *buf,
+			   unsigned int blk, size_t length)
+{
+	struct de2_hdmi_priv *priv = data;
+	int ret;
+
+	mutex_lock(&priv->mutex);
+	ret = hdmi_io_ddc_read(priv, blk / 2, (blk & 1) ? 128 : 0,
+				length, buf);
+	mutex_unlock(&priv->mutex);
+
+	return ret;
+}
+
+/* values duplicated from edid_cea_modes[] */
+static const struct drm_display_mode lmodes_tb[] = {
+	/* 2 - 720x480 at 60Hz */
+	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
+		   798, 858, 0, 480, 489, 495, 525, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
+	/* 4 - 1280x720 at 60Hz */
+	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+		   1430, 1650, 0, 720, 725, 730, 750, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+	/* 16 - 1920x1080 at 60Hz */
+	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+};
+
+static int de2_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct de2_hdmi_priv *priv = conn_to_priv(connector);
+	struct drm_display_mode *mode;
+	const struct drm_display_mode *lmode;
+	struct edid *edid;
+	int n;
+
+	edid = drm_do_get_edid(connector, read_edid_block, priv);
+
+	if (!edid) {
+		dev_warn(priv->dev, "failed to read EDID\n");
+		if (!connector->cmdline_mode.specified)
+			return 0;
+
+		if (connector->cmdline_mode.xres == 1920 &&
+		    connector->cmdline_mode.yres == 1080)
+			lmode = &lmodes_tb[2];
+		else if (connector->cmdline_mode.xres == 1280 &&
+			 connector->cmdline_mode.yres == 720)
+			lmode = &lmodes_tb[1];
+		else
+			lmode = &lmodes_tb[0];
+
+		mode = drm_mode_duplicate(connector->dev, lmode);
+		if (!mode)
+			return 0;
+		drm_mode_probed_add(connector, mode);
+
+		return 1;
+	}
+
+	drm_mode_connector_update_edid_property(connector, edid);
+	n = drm_add_edid_modes(connector, edid);
+
+	drm_edid_to_eld(connector, edid);
+
+	kfree(edid);
+
+	DRM_DEBUG_DRIVER("%s EDID ok %d modes\n",
+		connector->eld[0] ? "HDMI" : "DVI", n);
+
+	return n;
+}
+
+static const
+struct drm_connector_helper_funcs de2_hdmi_connector_helper_funcs = {
+	.get_modes = de2_hdmi_connector_get_modes,
+	.mode_valid = de2_hdmi_connector_mode_valid,
+};
+
+static const struct drm_connector_funcs de2_hdmi_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = de2_hdmi_connector_detect,
+	.destroy = drm_connector_cleanup,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static void de2_hdmi_cleanup(struct de2_hdmi_priv *priv)
+{
+	clk_disable_unprepare(priv->clk_ddc);
+	clk_disable_unprepare(priv->clk);
+	clk_disable_unprepare(priv->gate);
+	reset_control_assert(priv->reset1);
+	reset_control_assert(priv->reset0);
+}
+
+static int de2_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm = data;
+	struct de2_hdmi_priv *priv = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &priv->encoder;
+	struct drm_connector *connector = &priv->connector;
+	int ret;
+
+	encoder->possible_crtcs =
+			drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	/* if no CRTC, delay */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	/* HDMI init */
+	ret = reset_control_deassert(priv->reset0);
+	if (ret)
+		goto err;
+	ret = reset_control_deassert(priv->reset1);
+	if (ret)
+		goto err;
+
+	ret = clk_prepare_enable(priv->clk_ddc);
+	if (ret)
+		goto err;
+
+	de2_hdmi_set_clock(priv, 147500);	/* set a valid clock rate */
+	ret = clk_prepare_enable(priv->gate);
+	if (ret)
+		goto err;
+	ret = clk_prepare_enable(priv->clk);
+	if (ret)
+		goto err;
+
+	mutex_lock(&priv->mutex);
+	hdmi_io_init(priv);
+	mutex_unlock(&priv->mutex);
+
+	/* encoder init */
+	ret = drm_encoder_init(drm, encoder, &de2_hdmi_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS, NULL);
+	if (ret)
+		goto err;
+
+	drm_encoder_helper_add(encoder, &de2_hdmi_encoder_helper_funcs);
+
+	/* connector init */
+	ret = drm_connector_init(drm, connector,
+				 &de2_hdmi_connector_funcs,
+				 DRM_MODE_CONNECTOR_HDMIA);
+	if (ret)
+		goto err_connector;
+
+	connector->interlace_allowed = 1;
+	connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+				 DRM_CONNECTOR_POLL_DISCONNECT;
+	drm_connector_helper_add(connector,
+				 &de2_hdmi_connector_helper_funcs);
+
+	drm_mode_connector_attach_encoder(connector, encoder);
+
+	return 0;
+
+err_connector:
+	drm_encoder_cleanup(encoder);
+err:
+	dev_err(dev, "err %d\n", ret);
+	return ret;
+}
+
+static void de2_hdmi_unbind(struct device *dev, struct device *master,
+			   void *data)
+{
+	struct de2_hdmi_priv *priv = dev_get_drvdata(dev);
+
+	if (priv->connector.dev)
+		drm_connector_cleanup(&priv->connector);
+	drm_encoder_cleanup(&priv->encoder);
+	de2_hdmi_cleanup(priv);
+}
+
+static const struct component_ops de2_hdmi_ops = {
+	.bind = de2_hdmi_bind,
+	.unbind = de2_hdmi_unbind,
+};
+
+static int de2_hdmi_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct de2_hdmi_priv *priv;
+	struct resource *res;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	priv->dev = dev;
+
+	mutex_init(&priv->mutex);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "failed to get memory resource\n");
+		return -ENXIO;
+	}
+	priv->mmio = devm_ioremap_resource(dev, res);
+	if (IS_ERR(priv->mmio)) {
+		ret = PTR_ERR(priv->mmio);
+		dev_err(dev, "failed to map registers err %d\n", ret);
+		return ret;
+	}
+
+	priv->gate = devm_clk_get(dev, "bus");
+	if (IS_ERR(priv->gate)) {
+		ret = PTR_ERR(priv->gate);
+		dev_err(dev, "gate clock err %d\n", ret);
+		return ret;
+	}
+
+	priv->clk = devm_clk_get(dev, "clock");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		dev_err(dev, "hdmi clock err %d\n", ret);
+		return ret;
+	}
+
+	priv->clk_ddc = devm_clk_get(dev, "ddc-clock");
+	if (IS_ERR(priv->clk_ddc)) {
+		ret = PTR_ERR(priv->clk_ddc);
+		dev_err(dev, "hdmi-ddc clock err %d\n", ret);
+		return ret;
+	}
+
+	priv->reset0 = devm_reset_control_get(dev, "hdmi0");
+	if (IS_ERR(priv->reset0)) {
+		ret = PTR_ERR(priv->reset0);
+		dev_err(dev, "reset controller err %d\n", ret);
+		return ret;
+	}
+
+	priv->reset1 = devm_reset_control_get(dev, "hdmi1");
+	if (IS_ERR(priv->reset1)) {
+		ret = PTR_ERR(priv->reset1);
+		dev_err(dev, "reset controller err %d\n", ret);
+		return ret;
+	}
+
+	priv->soc_type = (int) of_match_device(de2_hdmi_dt_ids,
+						&pdev->dev)->data;
+
+	de2_hdmi_audio_register(dev);
+
+	return component_add(dev, &de2_hdmi_ops);
+}
+
+static int de2_hdmi_remove(struct platform_device *pdev)
+{
+	de2_hdmi_audio_unregister(&pdev->dev);
+	component_del(&pdev->dev, &de2_hdmi_ops);
+
+	return 0;
+}
+
+static struct platform_driver de2_hdmi_driver = {
+	.probe = de2_hdmi_probe,
+	.remove = de2_hdmi_remove,
+	.driver = {
+		.name = "sun8i-de2-hdmi",
+		.of_match_table = of_match_ptr(de2_hdmi_dt_ids),
+	},
+};
+
+/* create the video HDMI driver and the sound card driver */
+static int __init de2_hdmi_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&de2_hdmi_driver);
+
+	return ret;
+}
+
+static void __exit de2_hdmi_fini(void)
+{
+	platform_driver_unregister(&de2_hdmi_driver);
+}
+
+module_init(de2_hdmi_init);
+module_exit(de2_hdmi_fini);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("Allwinner DE2 HDMI encoder/connector");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi.h b/drivers/gpu/drm/sun8i/de2_hdmi.h
new file mode 100644
index 0000000..6711a76
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi.h
@@ -0,0 +1,51 @@
+#ifndef __DE2_HDMI_H__
+#define __DE2_HDMI_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <drm/drmP.h>
+
+/* SoC types */
+#define SOC_A83T 0
+#define SOC_H3 1
+
+struct de2_hdmi_priv {
+	struct device *dev;
+	void __iomem *mmio;
+
+	struct drm_encoder encoder;
+	struct drm_connector connector;
+
+	struct clk *clk;
+	struct clk *clk_ddc;
+	struct clk *gate;
+	struct reset_control *reset0;
+	struct reset_control *reset1;
+
+	struct mutex mutex;
+	u8 soc_type;
+	u8 cea_mode;
+};
+
+/* in de2_hdmi_io.c */
+void hdmi_io_init(struct de2_hdmi_priv *priv);
+void hdmi_io_video_on(struct de2_hdmi_priv *priv);
+void hdmi_io_video_off(struct de2_hdmi_priv *priv);
+void hdmi_io_mode_set(struct de2_hdmi_priv *priv,
+			struct drm_display_mode *mode);
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+			char pointer, char offset,
+			int nbyte, char *pbuf);
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv);
+int hdmi_io_mode_valid(int cea_mode);
+
+#endif /* __DE2_HDMI_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi_io.c b/drivers/gpu/drm/sun8i/de2_hdmi_io.c
new file mode 100644
index 0000000..b746a52
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi_io.c
@@ -0,0 +1,843 @@
+/*
+ * Allwinner A83T and H3 HDMI lowlevel functions
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
+ *	Copyright (c) 2016 Allwinnertech Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+/*
+ * The HDMI controller in the A83T and H3 seems to be a
+ * Synopsys DesignWare HDMI controller.
+ * The PHYs are unknown.
+ * Documentation:
+ *	https://linux-sunxi.org/DWC_HDMI_Controller
+ *	https://www.synopsys.com/dw/doc.php/ds/c/dwc_hdmi_tx_csds.pdf
+ */
+
+#include <linux/hdmi.h>
+
+#include "de2_hdmi.h"
+
+static int hdmi_mode = 1;
+MODULE_PARM_DESC(de2_hdmi_mode, "Force HDMI mode.\n"
+"When set, if the display device is connected by HDMI, switch to this mode.\n"
+"When unset, stay in DVI mode (useful when screen overscan).\n");
+module_param_named(de2_hdmi_mode, hdmi_mode, int, 0644);
+
+/* guessed PHY registers */
+#define HDMI_PHY_LOCK_READ_REG	0x10010
+#define HDMI_PHY_CTRL_REG	0x10020
+#define HDMI_PHY_24_REG		0x10024
+#define HDMI_PHY_28_REG		0x10028
+#define HDMI_PHY_PLL_REG	0x1002c
+#define HDMI_PHY_CLK_REG	0x10030
+#define HDMI_PHY_34_REG		0x10034
+#define HDMI_PHY_STATUS_REG	0x10038
+
+/* DW registers (obfuscated addresses) */
+
+/* Interrupt Registers */
+#define R_0100_HDMI_IH_FC_STAT0 0x0010
+#define R_0101_HDMI_IH_FC_STAT1 0x0011
+#define R_0102_HDMI_IH_FC_STAT2 0x8010
+#define R_0103_HDMI_IH_AS_STAT0 0x8011
+#define R_0104_HDMI_IH_PHY_STAT0 0x0012
+#define R_0105_HDMI_IH_I2CM_STAT0 0x0013
+#define R_0106_HDMI_IH_CEC_STAT0 0x8012
+#define R_0107_HDMI_IH_VP_STAT0 0x8013
+#define R_0108_HDMI_IH_I2CMPHY_STAT0 0x4010
+#define R_01ff_HDMI_IH_MUTE 0xf01f
+
+/* Video Sample Registers */
+#define R_0200_HDMI_TX_INVID0 0x0800
+#define R_0201_HDMI_TX_INSTUFFING 0x0801
+#define R_0202_HDMI_TX_GYDATA0 0x8800
+#define R_0203_HDMI_TX_GYDATA1 0x8801
+#define R_0204_HDMI_TX_RCRDATA0 0x0802
+#define R_0205_HDMI_TX_RCRDATA1 0x0803
+#define R_0206_HDMI_TX_BCBDATA0 0x8802
+#define R_0207_HDMI_TX_BCBDATA1 0x8803
+
+/* Video Packetizer Registers */
+#define R_0801_HDMI_VP_PR_CD 0x0401
+#define R_0802_HDMI_VP_STUFF 0x8400
+#define R_0803_HDMI_VP_REMAP 0x8401
+#define R_0804_HDMI_VP_CONF 0x0402
+#define R_0807_HDMI_VP_MASK 0x8403
+
+/* Frame Composer Registers */
+#define R_1000_HDMI_FC_INVIDCONF 0x0040
+#define		HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH 0x10
+#define		HDMI_FC_INVIDCONF_HDMI_MODE 0x08
+#define R_1001_HDMI_FC_INHACTV0 0x0041
+#define R_1002_HDMI_FC_INHACTV1 0x8040
+#define R_1003_HDMI_FC_INHBLANK0 0x8041
+#define R_1004_HDMI_FC_INHBLANK1 0x0042
+#define R_1005_HDMI_FC_INVACTV0 0x0043
+#define R_1006_HDMI_FC_INVACTV1 0x8042
+#define R_1007_HDMI_FC_INVBLANK 0x8043
+#define R_1008_HDMI_FC_HSYNCINDELAY0 0x4040
+#define R_1009_HDMI_FC_HSYNCINDELAY1 0x4041
+#define R_100a_HDMI_FC_HSYNCINWIDTH0 0xc040
+#define R_100b_HDMI_FC_HSYNCINWIDTH1 0xc041
+#define R_100c_HDMI_FC_VSYNCINDELAY 0x4042
+#define R_100d_HDMI_FC_VSYNCINWIDTH 0x4043
+#define R_1011_HDMI_FC_CTRLDUR 0x0045
+#define R_1012_HDMI_FC_EXCTRLDUR 0x8044
+#define R_1013_HDMI_FC_EXCTRLSPAC 0x8045
+#define R_1014_HDMI_FC_CH0PREAM 0x0046
+#define R_1015_HDMI_FC_CH1PREAM 0x0047
+#define R_1016_HDMI_FC_CH2PREAM 0x8046
+#define R_1018_HDMI_FC_GCP 0x4044
+#define R_1019_HDMI_FC_AVICONF0 0x4045
+#define		HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN 0x20
+#define R_101a_HDMI_FC_AVICONF1 0xc044
+#define R_101b_HDMI_FC_AVICONF2 0xc045
+#define R_101c_HDMI_FC_AVIVID 0x4046
+#define R_1025_HDMI_FC_AUDICONF0 0x2043
+#define R_1026_HDMI_FC_AUDICONF1 0xa042
+#define R_1027_HDMI_FC_AUDICONF2 0xa043
+#define R_1028_HDMI_FC_AUDICONF3 0x6040
+#define R_1029_HDMI_FC_VSDIEEEID0 0x6041
+#define R_1030_HDMI_FC_VSDIEEEID1 0x2044
+#define R_1031_HDMI_FC_VSDIEEEID2 0x2045
+#define R_1032_HDMI_FC_VSDPAYLOAD0 0xa044
+#define R_1033_HDMI_FC_VSDPAYLOAD1 0xa045
+#define R_1034_HDMI_FC_VSDPAYLOAD2 0x2046
+#define R_1063_HDMI_FC_AUDSCONF 0xa049
+#define R_1065_HDMI_FC_AUDSV 0x204b
+#define R_1066_HDMI_FC_AUDSU 0xa04a
+#define R_1067_HDMI_FC_AUDSCHNLS0 0xa04b
+#define		HDMI_FC_AUDSCHNLS0_CGMSA 0x30
+#define R_1068_HDMI_FC_AUDSCHNLS1 0x6048
+#define R_1069_HDMI_FC_AUDSCHNLS2 0x6049
+#define R_106a_HDMI_FC_AUDSCHNLS3 0xe048
+#define		HDMI_FC_AUDSCHNLS3_OIEC_CH0(v) (v)
+#define		HDMI_FC_AUDSCHNLS3_OIEC_CH1(v) (v << 4)
+#define R_106b_HDMI_FC_AUDSCHNLS4 0xe049
+#define		HDMI_FC_AUDSCHNLS4_OIEC_CH2(v) (v)
+#define		HDMI_FC_AUDSCHNLS4_OIEC_CH3(v) (v << 4)
+#define R_106c_HDMI_FC_AUDSCHNLS5 0x604a
+#define		HDMI_FC_AUDSCHNLS5_OIEC_CH0(v) (v)
+#define		HDMI_FC_AUDSCHNLS5_OIEC_CH1(v) (v << 4)
+#define R_106d_HDMI_FC_AUDSCHNLS6 0x604b
+#define		HDMI_FC_AUDSCHNLS6_OIEC_CH2(v) (v)
+#define		HDMI_FC_AUDSCHNLS6_OIEC_CH3(v) (v << 4)
+#define R_106e_HDMI_FC_AUDSCHNLS7 0xe04a
+#define R_106f_HDMI_FC_AUDSCHNLS8 0xe04b
+#define		HDMI_FC_AUDSCHNLS8_WORDLENGTH(v) (v)
+#define R_10b3_HDMI_FC_DATAUTO0 0xb045
+#define R_10b4_HDMI_FC_DATAUTO1 0x3046
+#define R_10b5_HDMI_FC_DATAUTO2 0x3047
+#define R_10d2_HDMI_FC_MASK0 0x904c
+#define R_10d6_HDMI_FC_MASK1 0x904e
+#define R_10da_HDMI_FC_MASK2 0xd04c
+#define R_10e0_HDMI_FC_PRCONF 0x3048
+#define R_1103_HDMI_FC_GMD_CONF 0x8051
+#define R_1104_HDMI_FC_GMD_HB 0x0052
+#define R_1200_HDMI_FC_DBGFORCE 0x0840
+#define		HDMI_FC_DBGFORCE_FORCEAUDIO BIT(4)
+#define		HDMI_FC_DBGFORCE_FORCEVIDEO BIT(0)
+#define R_1219_HDMI_FC_DBGTMDS0 0x4845
+
+/* HDMI Source PHY Registers */
+#define R_3000_HDMI_PHY_CONF0 0x0240
+#define		HDMI_PHY_CONF0_PDZ BIT(7)
+#define		HDMI_PHY_CONF0_ENTMDS BIT(6)
+#define		HDMI_PHY_CONF0_SPARECTRL BIT(5)
+#define		HDMI_PHY_CONF0_GEN2_PDDQ BIT(4)
+#define		HDMI_PHY_CONF0_GEN2_TXPWRON BIT(3)
+#define		HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE BIT(2)
+#define		HDMI_PHY_CONF0_SELDATAENPOL BIT(1)
+#define		HDMI_PHY_CONF0_SELDIPIF BIT(0)
+#define R_3001_HDMI_PHY_TST0 0x0241
+#define		HDMI_PHY_TST0_TSTCLR BIT(5)
+#define R_3005_HDMI_PHY_INT0 0x0243
+#define R_3006_HDMI_PHY_MASK0 0x8242
+
+/* HDMI Master PHY Registers */
+#define R_3020_HDMI_PHY_I2CM_SLAVE_ADDR 0x2240
+#define		HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 0x69
+#define R_3021_HDMI_PHY_I2CM_ADDRESS_ADDR 0x2241
+#define R_3022_HDMI_PHY_I2CM_DATAO_1_ADDR 0xa240
+#define R_3023_HDMI_PHY_I2CM_DATAO_0_ADDR 0xa241
+#define R_3026_HDMI_PHY_I2CM_OPERATION_ADDR 0xa242
+#define		HDMI_PHY_I2CM_OPERATION_ADDR_WRITE 0x10
+#define R_3027_HDMI_PHY_I2CM_INT_ADDR 0xa243
+#define R_3028_HDMI_PHY_I2CM_CTLINT_ADDR 0x6240
+
+/* Audio Sampler Registers */
+#define R_3100_HDMI_AUD_CONF0 0x0250
+#define		HDMI_AUD_CONF0_SW_RESET 0x80
+#define		HDMI_AUD_CONF0_I2S_ALL_ENABLE 0x2f
+#define R_3101_HDMI_AUD_CONF1 0x0251
+#define R_3102_HDMI_AUD_INT 0x8250
+#define R_3103_HDMI_AUD_CONF2 0x8251
+#define R_3200_HDMI_AUD_N1 0x0a40
+#define R_3201_HDMI_AUD_N2 0x0a41
+#define R_3202_HDMI_AUD_N3 0x8a40
+#define R_3205_HDMI_AUD_CTS3 0x0a43
+#define R_3206_HDMI_AUD_INPUTCLKFS 0x8a42
+#define		HDMI_AUD_INPUTCLKFS_64FS 0x04
+#define R_3302_HDMI_AUD_SPDIFINT 0x8a50
+
+/* Generic Parallel Audio Interface Registers */
+#define R_3506_HDMI_GP_POL 0x8272
+
+/* Main Controller Registers */
+#define R_4001_HDMI_MC_CLKDIS 0x0081
+#define		HDMI_MC_CLKDIS_HDCPCLK_DISABLE BIT(6)
+#define		HDMI_MC_CLKDIS_AUDCLK_DISABLE BIT(3)
+#define		HDMI_MC_CLKDIS_TMDSCLK_DISABLE BIT(1)
+#define R_4002_HDMI_MC_SWRSTZ 0x8080
+#define R_4004_HDMI_MC_FLOWCTRL 0x0082
+#define R_4005_HDMI_MC_PHYRSTZ 0x0083
+#define		HDMI_MC_PHYRSTZ_DEASSERT BIT(0)
+
+/* HDCP Encryption Engine Registers */
+#define R_5000_HDMI_A_HDCPCFG0 0x00c0
+#define R_5001_HDMI_A_HDCPCFG1 0x00c1
+#define		HDMI_A_HDCPCFG1_PH2UPSHFTENC BIT(2)
+#define		HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE BIT(1)
+#define		HDMI_A_HDCPCFG1_SWRESET BIT(0)
+#define R_5008_HDMI_A_APIINTMSK 0x40c0
+#define R_5009_HDMI_A_VIDPOLCFG 0x40c1
+#define		HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH BIT(4)
+
+/* CEC Engine Registers */
+#define R_7d02_HDMI_CEC_MASK 0x86f0
+
+/* I2C Master Registers (E-DDC) */
+#define R_7e00_HDMI_I2CM_SLAVE 0x0ee0
+#define R_7e01_HDMI_I2CM_ADDRESS 0x0ee1
+#define R_7e03_HDMI_I2CM_DATAI 0x8ee1
+#define R_7e04_HDMI_I2CM_OPERATION 0x0ee2
+#define		HDMI_I2CM_OPERATION_DDC_READ 0x02
+#define R_7e05_HDMI_I2CM_INT 0x0ee3
+#define R_7e06_HDMI_I2CM_CTLINT 0x8ee2
+#define R_7e07_HDMI_I2CM_DIV 0x8ee3
+#define R_7e08_HDMI_I2CM_SEGADDR 0x4ee0
+#define R_7e09_HDMI_I2CM_SOFTRSTZ 0x4ee1
+#define R_7e0a_HDMI_I2CM_SEGPTR 0xcee0
+#define R_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x4ee2
+#define R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0xcee2
+
+#define VIC_720x480_60		2
+#define VIC_1280x720_60		4
+#define VIC_1920x1080i_60	5
+#define VIC_720x480i_60		6
+#define VIC_1920x1080_60	16
+#define VIC_720x576_50		17
+#define VIC_1280x720_50		19
+#define VIC_1920x1080i_50	20
+#define VIC_720x576i_50		21
+#define VIC_1920x1080_50	31
+#define VIC_1920x1080_24	32
+#define VIC_1920x1080_25	33
+#define VIC_1920x1080_30	34
+
+static inline u8 hdmi_readb(struct de2_hdmi_priv *priv, u32 addr)
+{
+	return readb_relaxed(priv->mmio + addr);
+}
+
+static inline u32 hdmi_readl(struct de2_hdmi_priv *priv, u32 addr)
+{
+	return readl_relaxed(priv->mmio + addr);
+}
+
+static inline void hdmi_writeb(struct de2_hdmi_priv *priv, u32 addr, u8 data)
+{
+	writeb_relaxed(data, priv->mmio + addr);
+}
+
+static inline void hdmi_writel(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+	writel_relaxed(data, priv->mmio + addr);
+}
+
+static inline void hdmi_orb(struct de2_hdmi_priv *priv, u32 addr, u8 data)
+{
+	writeb_relaxed(readb_relaxed(priv->mmio + addr) | data,
+			priv->mmio + addr);
+}
+
+static inline void hdmi_orl(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+	writel_relaxed(readl_relaxed(priv->mmio + addr) | data,
+			priv->mmio + addr);
+}
+
+static inline void hdmi_andl(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+	writel_relaxed(readl_relaxed(priv->mmio + addr) & data,
+			priv->mmio + addr);
+}
+
+/* read on/off functions */
+static inline void hdmi_read_on(struct de2_hdmi_priv *priv)
+{
+	hdmi_writel(priv, HDMI_PHY_LOCK_READ_REG, 0x54524545);
+}
+static inline void hdmi_read_off(struct de2_hdmi_priv *priv)
+{
+	hdmi_writel(priv, HDMI_PHY_LOCK_READ_REG, 0x57415452);
+}
+
+static void hdmi_inner_init(struct de2_hdmi_priv *priv)
+{
+	u8 clkdis = priv->soc_type == SOC_H3 ?
+				~HDMI_MC_CLKDIS_TMDSCLK_DISABLE : 0xff;
+
+	hdmi_read_on(priv);
+
+	/* software reset */
+	hdmi_writeb(priv, R_4002_HDMI_MC_SWRSTZ,  0x00);
+	udelay(2);
+
+	/* mask all interrupts */
+	hdmi_writeb(priv, R_01ff_HDMI_IH_MUTE, 0x00);
+	hdmi_writeb(priv, R_0807_HDMI_VP_MASK, 0xff);
+	hdmi_writeb(priv, R_10d2_HDMI_FC_MASK0, 0xff);
+	hdmi_writeb(priv, R_10d6_HDMI_FC_MASK1, 0xff);
+	hdmi_writeb(priv, R_10da_HDMI_FC_MASK2, 0xff);
+	hdmi_writeb(priv, R_3102_HDMI_AUD_INT, 0xff);
+	hdmi_writeb(priv, R_3302_HDMI_AUD_SPDIFINT, 0xff);
+	hdmi_writeb(priv, R_3506_HDMI_GP_POL, 0xff);
+	hdmi_writeb(priv, R_5008_HDMI_A_APIINTMSK, 0xff);
+	hdmi_writeb(priv, R_7d02_HDMI_CEC_MASK, 0xff);
+	hdmi_writeb(priv, R_7e05_HDMI_I2CM_INT, 0xff);
+	hdmi_writeb(priv, R_7e06_HDMI_I2CM_CTLINT, 0xff);
+
+	hdmi_writeb(priv, R_1063_HDMI_FC_AUDSCONF, 0xf0);
+	hdmi_writeb(priv, R_10b3_HDMI_FC_DATAUTO0, 0x1e);
+	hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1, 0x00);
+	hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1,
+				HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE |
+				HDMI_A_HDCPCFG1_SWRESET);
+	hdmi_writeb(priv, R_5000_HDMI_A_HDCPCFG0, 0x00);
+	hdmi_writeb(priv, R_5009_HDMI_A_VIDPOLCFG,
+				HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH);
+	hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, clkdis);
+	hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00);
+	hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, clkdis);
+	hdmi_writeb(priv, R_0100_HDMI_IH_FC_STAT0, 0xff);
+	hdmi_writeb(priv, R_0101_HDMI_IH_FC_STAT1, 0xff);
+	hdmi_writeb(priv, R_0102_HDMI_IH_FC_STAT2, 0xff);
+	hdmi_writeb(priv, R_0103_HDMI_IH_AS_STAT0, 0xff);
+	hdmi_writeb(priv, R_0105_HDMI_IH_I2CM_STAT0, 0xff);
+	hdmi_writeb(priv, R_0106_HDMI_IH_CEC_STAT0, 0xff);
+	hdmi_writeb(priv, R_0107_HDMI_IH_VP_STAT0, 0xff);
+}
+
+static void hdmi_phy_init_a83t(struct de2_hdmi_priv *priv)
+{
+	hdmi_inner_init(priv);
+
+	hdmi_writeb(priv, 0x10000, 0x01);
+	hdmi_writeb(priv, 0x10001, 0x00);
+	hdmi_writeb(priv, 0x10002, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+	hdmi_writeb(priv, 0x10003, 0x00);
+	hdmi_writeb(priv, 0x10007, 0xa0);
+	hdmi_writeb(priv, R_4005_HDMI_MC_PHYRSTZ,
+					HDMI_MC_PHYRSTZ_DEASSERT);
+	udelay(1);
+	hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+					HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+					HDMI_PHY_CONF0_SELDATAENPOL);
+	hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+					HDMI_PHY_CONF0_GEN2_PDDQ |
+					HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+					HDMI_PHY_CONF0_SELDATAENPOL);
+	hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+					HDMI_PHY_CONF0_GEN2_PDDQ |
+					HDMI_PHY_CONF0_SELDATAENPOL);
+	hdmi_writeb(priv, R_3006_HDMI_PHY_MASK0, 0xf0);
+	hdmi_writeb(priv, R_3027_HDMI_PHY_I2CM_INT_ADDR, 0xff);
+	hdmi_writeb(priv, R_3028_HDMI_PHY_I2CM_CTLINT_ADDR, 0xff);
+	hdmi_writeb(priv, R_0104_HDMI_IH_PHY_STAT0, 0xff);
+	hdmi_writeb(priv, R_0108_HDMI_IH_I2CMPHY_STAT0, 0xff);
+	hdmi_writeb(priv, R_4005_HDMI_MC_PHYRSTZ, 0x00);
+	hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+					HDMI_PHY_CONF0_GEN2_PDDQ |
+					HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+					HDMI_PHY_CONF0_SELDATAENPOL);
+	hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+					HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+					HDMI_PHY_CONF0_SELDATAENPOL);
+	hdmi_writeb(priv, R_3001_HDMI_PHY_TST0, HDMI_PHY_TST0_TSTCLR);
+	hdmi_writeb(priv, R_3020_HDMI_PHY_I2CM_SLAVE_ADDR,
+					HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+	hdmi_writeb(priv, R_3001_HDMI_PHY_TST0, 0x00);
+}
+
+static void hdmi_phy_init_h3(struct de2_hdmi_priv *priv)
+{
+	int to_cnt;
+	u32 tmp;
+
+	hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0);
+	hdmi_writel(priv, HDMI_PHY_CTRL_REG, 1 << 0);
+	udelay(5);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 16);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 1);
+	udelay(10);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 2);
+	udelay(5);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 3);
+	usleep_range(40, 50);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 19);
+	usleep_range(100, 120);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 18);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 7 << 4);
+
+	to_cnt = 10;
+	while (1) {
+		if (hdmi_readl(priv, HDMI_PHY_STATUS_REG) & 0x80)
+			break;
+		usleep_range(200, 250);
+		if (--to_cnt == 0) {
+			dev_err(priv->dev, "hdmi phy init timeout\n");
+			break;
+		}
+	}
+
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 0xf << 8);
+	hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 7);
+
+	hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+	hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084343);
+	msleep(20);
+	hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+	hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+	msleep(100);
+	tmp = hdmi_readl(priv, HDMI_PHY_STATUS_REG);
+	hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+	hdmi_orl(priv, HDMI_PHY_PLL_REG, (tmp >> 11) & 0x3f);
+	hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ff0f7f);
+	hdmi_writel(priv, HDMI_PHY_24_REG, 0x80639000);
+	hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+
+	hdmi_inner_init(priv);
+}
+
+static int get_divider(int rate)
+{
+	if (rate <= 27000)
+		return 11;
+	if (rate <= 74250)
+		return 4;
+	if (rate <= 148500)
+		return 2;
+	return 1;
+}
+
+static void hdmi_i2cm_write(struct de2_hdmi_priv *priv,
+			    int addr, u8 valh, u8 vall)
+{
+	hdmi_writeb(priv, R_3021_HDMI_PHY_I2CM_ADDRESS_ADDR, addr);
+	hdmi_writeb(priv, R_3022_HDMI_PHY_I2CM_DATAO_1_ADDR, valh);
+	hdmi_writeb(priv, R_3023_HDMI_PHY_I2CM_DATAO_0_ADDR, vall);
+	hdmi_writeb(priv, R_3026_HDMI_PHY_I2CM_OPERATION_ADDR,
+					HDMI_PHY_I2CM_OPERATION_ADDR_WRITE);
+	usleep_range(2000, 2500);
+}
+
+static void hdmi_phy_set_a83t(struct de2_hdmi_priv *priv,
+				struct drm_display_mode *mode)
+{
+	switch (get_divider(mode->clock)) {
+	case 1:
+		hdmi_i2cm_write(priv, 0x06, 0x00, 0x00);
+		hdmi_i2cm_write(priv, 0x15, 0x00, 0x0f);
+		hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+		hdmi_i2cm_write(priv, 0x19, 0x00, 0x02);
+		hdmi_i2cm_write(priv, 0x0e, 0x00, 0x00);
+		hdmi_i2cm_write(priv, 0x09, 0x80, 0x2b);
+		break;
+	case 2:				/* 1080P @ 60 & 50 */
+		hdmi_i2cm_write(priv, 0x06, 0x04, 0xa0);
+		hdmi_i2cm_write(priv, 0x15, 0x00, 0x0a);
+		hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+		hdmi_i2cm_write(priv, 0x19, 0x00, 0x02);
+		hdmi_i2cm_write(priv, 0x0e, 0x00, 0x21);
+		hdmi_i2cm_write(priv, 0x09, 0x80, 0x29);
+		break;
+	case 4:				/* 720P @ 50 & 60, 1080I, 1080 */
+		hdmi_i2cm_write(priv, 0x06, 0x05, 0x40);
+		hdmi_i2cm_write(priv, 0x15, 0x00, 0x05);
+		hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+		hdmi_i2cm_write(priv, 0x19, 0x00, 0x07);
+		hdmi_i2cm_write(priv, 0x0e, 0x02, 0xb5);
+		hdmi_i2cm_write(priv, 0x09, 0x80, 0x09);
+		break;
+/*	case 11:			* 480P/576P */
+	default:
+		hdmi_i2cm_write(priv, 0x06, 0x01,
+			mode->flags & DRM_MODE_FLAG_DBLCLK ? 0xe3 : 0xe0);
+		hdmi_i2cm_write(priv, 0x15, 0x00, 0x00);
+		hdmi_i2cm_write(priv, 0x10, 0x08, 0xda);
+		hdmi_i2cm_write(priv, 0x19, 0x00, 0x07);
+		hdmi_i2cm_write(priv, 0x0e, 0x03, 0x18);
+		hdmi_i2cm_write(priv, 0x09, 0x80, 0x09);
+		break;
+	}
+	hdmi_i2cm_write(priv, 0x1e, 0x00, 0x00);
+	hdmi_i2cm_write(priv, 0x13, 0x00, 0x00);
+	hdmi_i2cm_write(priv, 0x17, 0x00, 0x00);
+	hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+				HDMI_PHY_CONF0_GEN2_TXPWRON |
+				HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+				HDMI_PHY_CONF0_SELDATAENPOL);
+}
+
+static void hdmi_phy_set_h3(struct de2_hdmi_priv *priv,
+			struct drm_display_mode *mode)
+{
+	u32 tmp;
+
+	hdmi_andl(priv, HDMI_PHY_CTRL_REG, ~0xf000);
+
+	switch (get_divider(mode->clock)) {
+	case 1:
+		hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x31dc5fc0);
+						/* or 0x30dc5fc0 ? */
+		hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x800863c0);
+		msleep(20);
+		hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+		msleep(200);
+		tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+		if (tmp < 0x3d)
+			tmp += 2;
+		else
+			tmp = 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+		msleep(100);
+		hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+		hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+		hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f8246b5);
+		break;
+	case 2:				/* 1080P @ 60 & 50 */
+		hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+		hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084381);
+		msleep(20);
+		hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+		msleep(100);
+		tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+		hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+		hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063a800);
+		hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c485);
+		break;
+	case 4:				/* 720P @ 50 & 60, 1080I, 1080 */
+		hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+		hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084343);
+		msleep(20);
+		hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+		msleep(100);
+		tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+		hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+		hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+		hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+		break;
+	default:
+/*	case 11:				* 480P/576P */
+		hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+		hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x8008430a);
+		msleep(20);
+		hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+		msleep(100);
+		tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+		hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+		hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+		hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+		hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+		break;
+	}
+}
+
+/* HDMI functions */
+
+/* hardware init */
+void hdmi_io_init(struct de2_hdmi_priv *priv)
+{
+	if (priv->soc_type == SOC_H3)
+		hdmi_phy_init_h3(priv);
+	else
+		hdmi_phy_init_a83t(priv);
+
+	/* disable hdcp */
+	hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1,
+					HDMI_A_HDCPCFG1_PH2UPSHFTENC);
+	hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS,
+					HDMI_MC_CLKDIS_HDCPCLK_DISABLE);
+}
+
+/* check if the resolution is supported */
+int hdmi_io_mode_valid(int cea_mode)
+{
+	switch (cea_mode) {
+	case VIC_720x480_60:
+	case VIC_1280x720_60:
+	case VIC_1920x1080i_60:
+	case VIC_720x480i_60:
+	case VIC_1920x1080_60:
+	case VIC_720x576_50:
+	case VIC_1280x720_50:
+	case VIC_1920x1080i_50:
+	case VIC_720x576i_50:
+	case VIC_1920x1080_50:
+	case VIC_1920x1080_24:
+	case VIC_1920x1080_25:
+	case VIC_1920x1080_30:
+		return 1;
+	}
+	return -1;
+}
+
+/* output init */
+void hdmi_io_mode_set(struct de2_hdmi_priv *priv,
+			struct drm_display_mode *mode)
+{
+	int avi_d2;			/* AVI InfoFrame Data Byte 2 */
+	int h_blank, h_sync_w, h_front_p;
+	int invidconf;
+
+	/* colorimetry and aspect ratio */
+	switch (priv->cea_mode) {
+	case 0:
+		return;			/* bad mode */
+	case VIC_720x480_60:
+	case VIC_720x480i_60:
+	case VIC_720x576_50:
+	case VIC_720x576i_50:
+		avi_d2 = (HDMI_COLORIMETRY_ITU_601 << 6) |
+			(HDMI_PICTURE_ASPECT_4_3 << 4) | 0x08;
+		break;
+	default:
+		avi_d2 = (HDMI_COLORIMETRY_ITU_709 << 6) |
+			(HDMI_PICTURE_ASPECT_16_9 << 4) | 0x08;
+		break;
+	}
+
+	h_blank = mode->htotal - mode->hdisplay;
+	h_sync_w = mode->hsync_end - mode->hsync_start;
+	h_front_p = mode->hsync_start - mode->hdisplay;
+
+	invidconf = 0;
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		invidconf |= 0x01;
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		invidconf |= 0x20;
+	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+		invidconf |= 0x40;
+
+	if (priv->soc_type == SOC_H3) {
+		hdmi_phy_set_h3(priv, mode);
+		hdmi_inner_init(priv);
+	} else {
+		hdmi_phy_init_a83t(priv);
+	}
+
+	hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE,
+					HDMI_FC_DBGFORCE_FORCEVIDEO);
+	hdmi_writeb(priv, R_1219_HDMI_FC_DBGTMDS0, 0x00);
+	hdmi_writeb(priv, R_1000_HDMI_FC_INVIDCONF,
+				invidconf |
+				HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH);
+	hdmi_writeb(priv, 0x10001, invidconf < 0x60 ? 0x03 : 0x00);
+	hdmi_writeb(priv, R_1002_HDMI_FC_INHACTV1, mode->hdisplay >> 8);
+	hdmi_writeb(priv, R_100d_HDMI_FC_VSYNCINWIDTH,
+			mode->vsync_end - mode->vsync_start);
+	hdmi_writeb(priv, R_1006_HDMI_FC_INVACTV1, mode->vdisplay >> 8);
+	hdmi_writeb(priv, R_1004_HDMI_FC_INHBLANK1, h_blank >> 8);
+	hdmi_writeb(priv, R_100c_HDMI_FC_VSYNCINDELAY,
+			mode->vsync_start - mode->vdisplay);
+	hdmi_writeb(priv, R_1009_HDMI_FC_HSYNCINDELAY1, h_front_p >> 8);
+	hdmi_writeb(priv, R_100b_HDMI_FC_HSYNCINWIDTH1, h_sync_w >> 8);
+	hdmi_writeb(priv, R_1001_HDMI_FC_INHACTV0, mode->hdisplay);
+	hdmi_writeb(priv, R_1003_HDMI_FC_INHBLANK0, h_blank);
+	hdmi_writeb(priv, R_1008_HDMI_FC_HSYNCINDELAY0, h_front_p);
+	hdmi_writeb(priv, R_100a_HDMI_FC_HSYNCINWIDTH0, h_sync_w);
+	hdmi_writeb(priv, R_1005_HDMI_FC_INVACTV0, mode->vdisplay);
+	hdmi_writeb(priv, R_1007_HDMI_FC_INVBLANK,
+			mode->vtotal - mode->vdisplay);
+	hdmi_writeb(priv, R_1011_HDMI_FC_CTRLDUR, 12);
+	hdmi_writeb(priv, R_1012_HDMI_FC_EXCTRLDUR, 32);
+	hdmi_writeb(priv, R_1013_HDMI_FC_EXCTRLSPAC, 1);
+	hdmi_writeb(priv, R_1014_HDMI_FC_CH0PREAM, 0x0b);
+	hdmi_writeb(priv, R_1015_HDMI_FC_CH1PREAM, 0x16);
+	hdmi_writeb(priv, R_1016_HDMI_FC_CH2PREAM, 0x21);
+	hdmi_writeb(priv, R_10e0_HDMI_FC_PRCONF,
+			mode->flags & DRM_MODE_FLAG_DBLCLK ? 0x21 : 0x10);
+	hdmi_writeb(priv, R_0801_HDMI_VP_PR_CD,
+			mode->flags & DRM_MODE_FLAG_DBLCLK ? 0x41 : 0x40);
+	hdmi_writeb(priv, R_0802_HDMI_VP_STUFF, 0x07);
+	hdmi_writeb(priv, R_0803_HDMI_VP_REMAP, 0x00);
+	hdmi_writeb(priv, R_0804_HDMI_VP_CONF, 0x47);
+	hdmi_writeb(priv, R_0200_HDMI_TX_INVID0, 0x01);
+	hdmi_writeb(priv, R_0201_HDMI_TX_INSTUFFING, 0x07);
+	hdmi_writeb(priv, R_0202_HDMI_TX_GYDATA0, 0x00);
+	hdmi_writeb(priv, R_0203_HDMI_TX_GYDATA1, 0x00);
+	hdmi_writeb(priv, R_0204_HDMI_TX_RCRDATA0, 0x00);
+	hdmi_writeb(priv, R_0205_HDMI_TX_RCRDATA1, 0x00);
+	hdmi_writeb(priv, R_0206_HDMI_TX_BCBDATA0, 0x00);
+	hdmi_writeb(priv, R_0207_HDMI_TX_BCBDATA1, 0x00);
+
+	if (priv->connector.eld[0]) {		/* if audio/HDMI */
+		hdmi_writeb(priv, R_10b3_HDMI_FC_DATAUTO0, 0x08);
+		hdmi_writeb(priv, R_1031_HDMI_FC_VSDIEEEID2, 0x00);
+		hdmi_writeb(priv, R_1030_HDMI_FC_VSDIEEEID1,
+						HDMI_IEEE_OUI >> 8);
+		hdmi_writeb(priv, R_1029_HDMI_FC_VSDIEEEID0,
+						HDMI_IEEE_OUI & 0xff);
+		hdmi_writeb(priv, R_1032_HDMI_FC_VSDPAYLOAD0, 0x00);
+		hdmi_writeb(priv, R_1033_HDMI_FC_VSDPAYLOAD1, 0x00);
+		hdmi_writeb(priv, R_1034_HDMI_FC_VSDPAYLOAD2, 0x00);
+		hdmi_writeb(priv, R_10b4_HDMI_FC_DATAUTO1, 0x01);
+		hdmi_writeb(priv, R_10b5_HDMI_FC_DATAUTO2, 0x11);
+		hdmi_writeb(priv, R_1018_HDMI_FC_GCP, 0x00);
+		hdmi_writeb(priv, R_1104_HDMI_FC_GMD_HB, 0x00);
+		hdmi_writeb(priv, R_1103_HDMI_FC_GMD_CONF, 0x11);
+
+		/* switch to HDMI mode */
+		if (hdmi_mode) {
+			hdmi_read_on(priv);
+			hdmi_orb(priv, R_1000_HDMI_FC_INVIDCONF,
+					HDMI_FC_INVIDCONF_HDMI_MODE);
+			hdmi_read_off(priv);
+		}
+
+		/* AVI */
+		hdmi_writeb(priv, R_1019_HDMI_FC_AVICONF0,
+					HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN);
+		hdmi_writeb(priv, R_101a_HDMI_FC_AVICONF1, avi_d2);
+		hdmi_writeb(priv, R_101b_HDMI_FC_AVICONF2, 0x08);
+		hdmi_writeb(priv, R_101c_HDMI_FC_AVIVID, priv->cea_mode);
+	}
+
+	hdmi_writeb(priv, R_4004_HDMI_MC_FLOWCTRL, 0x00);
+	hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00);	/* enable all clocks */
+
+	if (priv->soc_type != SOC_H3)
+		hdmi_phy_set_a83t(priv, mode);
+
+	hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE, 0x00);
+
+}
+
+void hdmi_io_video_on(struct de2_hdmi_priv *priv)
+{
+	if (!priv->cea_mode)
+		return;
+pr_info("*jfm* hdmi video on\n");
+	if (priv->soc_type == SOC_H3)
+		hdmi_orl(priv, HDMI_PHY_CTRL_REG, 0x0f << 12);
+}
+
+void hdmi_io_video_off(struct de2_hdmi_priv *priv)
+{
+	if (!priv->cea_mode)
+		return;
+pr_info("*jfm* hdmi video off\n");
+	if (priv->soc_type == SOC_H3)
+		hdmi_andl(priv, HDMI_PHY_CTRL_REG, ~(0x0f << 12));
+}
+
+/* get a block of EDID */
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+			char pointer, char off,
+			int nbyte, char *pbuf)
+{
+	unsigned int to_cnt;
+	u8 reg;
+	int ret = 0;
+
+	hdmi_read_on(priv);
+	hdmi_writeb(priv, R_7e09_HDMI_I2CM_SOFTRSTZ, 0x00);
+	to_cnt = 50;
+	while (!(hdmi_readb(priv, R_7e09_HDMI_I2CM_SOFTRSTZ) & 0x01)) {
+		udelay(10);
+		if (--to_cnt == 0) {	/* wait for 500us for timeout */
+			dev_err(priv->dev, "hdmi ddc reset timeout\n");
+			break;
+		}
+	}
+
+	hdmi_writeb(priv, R_7e07_HDMI_I2CM_DIV, 0x05);
+	hdmi_writeb(priv, R_7e05_HDMI_I2CM_INT, 0x08);
+	hdmi_writeb(priv, R_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR, 0xd8);
+	hdmi_writeb(priv, R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR, 0xfe);
+
+	while (nbyte > 0) {
+		hdmi_writeb(priv, R_7e00_HDMI_I2CM_SLAVE, 0xa0 >> 1);
+		hdmi_writeb(priv, R_7e01_HDMI_I2CM_ADDRESS, off);
+		hdmi_writeb(priv, R_7e08_HDMI_I2CM_SEGADDR, 0x60 >> 1);
+		hdmi_writeb(priv, R_7e0a_HDMI_I2CM_SEGPTR, pointer);
+		hdmi_writeb(priv, R_7e04_HDMI_I2CM_OPERATION,
+					HDMI_I2CM_OPERATION_DDC_READ);
+
+		to_cnt = 200;				/* timeout 100ms */
+		while (1) {
+			reg = hdmi_readb(priv, R_0105_HDMI_IH_I2CM_STAT0);
+			hdmi_writeb(priv, R_0105_HDMI_IH_I2CM_STAT0, reg);
+			if (reg & 0x02) {
+				*pbuf++ = hdmi_readb(priv,
+						R_7e03_HDMI_I2CM_DATAI);
+				break;
+			}
+			if (reg & 0x01) {
+				dev_err(priv->dev, "hdmi ddc read error\n");
+				ret = -1;
+				break;
+			}
+			if (--to_cnt == 0) {
+				if (!ret) {
+					dev_err(priv->dev,
+						"hdmi ddc read timeout\n");
+					ret = -1;
+				}
+				break;
+			}
+			usleep_range(500, 800);
+		}
+		if (ret)
+			break;
+		nbyte--;
+		off++;
+	}
+	hdmi_read_off(priv);
+
+	return ret;
+}
+
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv)
+{
+	int ret;
+
+	hdmi_read_on(priv);
+
+	if (priv->soc_type == SOC_H3)
+		ret = hdmi_readl(priv, HDMI_PHY_STATUS_REG) & 0x80000;
+	else
+		ret = hdmi_readb(priv, R_3005_HDMI_PHY_INT0) & 0x02;
+
+	hdmi_read_off(priv);
+
+	return ret != 0;
+}
-- 
2.10.2

^ permalink raw reply related

* [PATCH] PM / Domains: Fix compatible for domain idle state
From: Ulf Hansson @ 2016-11-29  8:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161110195832.4nz7lxlmshaemcbb@rob-hp-laptop>

On 10 November 2016 at 20:58, Rob Herring <robh@kernel.org> wrote:
> On Mon, Nov 07, 2016 at 12:14:28PM +0100, Ulf Hansson wrote:
>> On 3 November 2016 at 22:54, Lina Iyer <lina.iyer@linaro.org> wrote:
>> > Re-using idle state definition provided by arm,idle-state for domain
>> > idle states creates a lot of confusion and limits further evolution of
>> > the domain idle definition. To keep things clear and simple, define a
>> > idle states for domain using a new compatible "domain-idle-state".
>> >
>> > Fix existing PM domains code to look for the newly defined compatible.
>> >
>> > Cc: <devicetree@vger.kernel.org>
>> > Cc: Rob Herring <robh@kernel.org>
>> > Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>> > ---
>> >  .../bindings/power/domain-idle-state.txt           | 33 ++++++++++++++++++++++
>> >  .../devicetree/bindings/power/power_domain.txt     |  8 +++---
>> >  drivers/base/power/domain.c                        |  2 +-
>> >  3 files changed, 38 insertions(+), 5 deletions(-)
>> >  create mode 100644 Documentation/devicetree/bindings/power/domain-idle-state.txt
>> >
>> > diff --git a/Documentation/devicetree/bindings/power/domain-idle-state.txt b/Documentation/devicetree/bindings/power/domain-idle-state.txt
>> > new file mode 100644
>> > index 0000000..eefc7ed
>> > --- /dev/null
>> > +++ b/Documentation/devicetree/bindings/power/domain-idle-state.txt
>> > @@ -0,0 +1,33 @@
>> > +PM Domain Idle State Node:
>> > +
>> > +A domain idle state node represents the state parameters that will be used to
>> > +select the state when there are no active components in the domain.
>> > +
>> > +The state node has the following parameters -
>> > +
>> > +- compatible:
>> > +       Usage: Required
>> > +       Value type: <string>
>> > +       Definition: Must be "domain-idle-state".
>> > +
>> > +- entry-latency-us
>> > +       Usage: Required
>> > +       Value type: <prop-encoded-array>
>> > +       Definition: u32 value representing worst case latency in
>> > +                   microseconds required to enter the idle state.
>> > +                   The exit-latency-us duration may be guaranteed
>> > +                   only after entry-latency-us has passed.
>>
>> As we anyway are going to change this, why not use an u64 and have the
>> value in ns instead of us?
>
> I can't imagine that you would need more resolution or range. For times
> less than 1us, s/w and register access times are going to dominate the
> time.
>
> Unless there is a real need, I'd keep alignment with the existing
> binding.

Rob, are you fine with this? I thought it would be great to get this
in for 4.10 rc1.

Kind regards
Uffe

^ permalink raw reply

* [PATCH v2 1/7] MFD: add bindings for stm32 general purpose timer driver
From: Benjamin Gaignard @ 2016-11-29  8:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <b78a21f7-38a1-5a40-b96e-d1c9156aee68@kernel.org>

2016-11-27 16:41 GMT+01:00 Jonathan Cameron <jic23@kernel.org>:
> On 27/11/16 14:10, Jonathan Cameron wrote:
>> On 24/11/16 15:14, Benjamin Gaignard wrote:
>>> Add bindings information for stm32 general purpose timer
>>>
>>> version 2:
>>> - rename stm32-mfd-timer to stm32-gptimer
>>> - only keep one compatible string
>>>
>>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
>>> ---
>>>  .../bindings/mfd/stm32-general-purpose-timer.txt   | 43 ++++++++++++++++++++++
>>>  1 file changed, 43 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt b/Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt
>>> new file mode 100644
>>> index 0000000..2f10e67
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/mfd/stm32-general-purpose-timer.txt
>>> @@ -0,0 +1,43 @@
>>> +STM32 general purpose timer driver
>>> +
>>> +Required parameters:
>>> +- compatible: must be "st,stm32-gptimer"
>>> +
>>> +- reg:                      Physical base address and length of the controller's
>>> +                    registers.
>>> +- clock-names:              Set to "clk_int".
>>> +- clocks:           Phandle to the clock used by the timer module.
>>> +                    For Clk properties, please refer to ../clock/clock-bindings.txt
>>> +
>>> +Optional parameters:
>>> +- resets:           Phandle to the parent reset controller.
>>> +                    See ..reset/st,stm32-rcc.txt
>>> +
>>> +Optional subnodes:
>>> +- pwm:                      See ../pwm/pwm-stm32.txt
>>> +- iiotimer:         See ../iio/timer/stm32-iio-timer.txt
>> Naming issue here.  Can't mention IIO as that's a linux subsystem and all
>> bindings must be independent of OS.
>>
>> Perhaps adc-trigger-timer?
>>> +
>>> +Example:
>>> +    gptimer1: gptimer1 at 40010000 {
>>> +            compatible = "st,stm32-gptimer";
>>> +            reg = <0x40010000 0x400>;
>>> +            clocks = <&rcc 0 160>;
>>> +            clock-names = "clk_int";
>>> +
>>> +            pwm1 at 0 {
>>> +                    compatible = "st,stm32-pwm";
>>> +                    st,pwm-num-chan = <4>;
>>> +                    st,breakinput;
>>> +                    st,complementary;
>>> +            };
>>> +
>>> +            iiotimer1 at 0 {
>>> +                    compatible = "st,stm32-iio-timer";
>> Again, avoid the use of iio in here (same issue you had with mfd in the previous
>> version).
>>> +                    interrupts = <27>;
>>> +                    st,input-triggers-names = TIM5_TRGO,
>> Docs for these should be introduced before they are used in an example.
>> Same for the PWM ones above.  Expand the detail fo the example as you add
>> the other elements.
>
> I've just dived into the datasheet for these timers.
> http://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf
>

I really appreciate that you do this effort, thanks.

>
> I think you need a binding that describes the capabilities of each of the timers
> explicitly.   Down to the level of whether there is a repetition counter or not.
> Each should exists as a separate entity in device tree.
>
> They then have an existence as timers separate to the description of what they
> are used for.
>
> Here the only way we are saying they exist is by their use which doesn't feel
> right to me.
>
> So I think you need to move back to what you had in the first place.  The key
> thing is that ever timer needs describing fully.  They are different enough
> that for example the datasheet doesn't even try to describe them in one section.
> (it has 4 separate chapters covering different sets of these hardware blocks).
> The naming isn't really based on index, we are talking different hardware
> that the datasheet authors have decided not to give different names to!

Even if the hardware are named differently in the documentation they
all share the
same registers definitions and mapping but configurations are
different for each device.

>
> If they'd called them
> advanced timers
> generic timers
> basic timers
> really basic timers meant for driving the DAC (6 and 7)
>
> We'd all have been quite happy with different compatible strings giving away
> what they can do.

4 compatible strings will not be enough to describe devices
configuration, for example
in the documentation general purpose timers could have a 16 or 32 bit
counter, for 1 to 4
pwm channels and triggers (accepted or generated by the device) are
different for each device.

DAC could be drive by timers 2, 4, 5, 6, 7 and 8.
ADC could be driver by 32 triggers

> What you have here is far too specific to what you are trying to do with them
> right now.
>
> These things are separately capable of timing capture (which is I guess where
> the IIO device later comes in).
>
> So my expectation is that we end up potentially instantiating:
>
> 1) An MFD to handle the shared elements of the timers.
> 2) Up to 12ish timers each with separate existence as a device in the driver model
> and in device tree.
> (nasty corner cases here are using timers as perscalers for other timers - I'd be
> tempted to leave that for now)
> Note that each of these devices has a different register set I think? Any shared
> bits are handled via the mfd above (if we even need that MFD which I'm starting
> to doubt looking at the datasheet).
>

pwm and trigger share the same registers but not the same bits.
With regmap write functions I don't have sharing problems.

> 3) Up to N pwms again with there own existence in the device model.  These don't
> do much other than wrap the timer and stick it in output mode.
> 4) Up to N iio triggers - this is basically using the timer as a periodic interrupt
> (though without the interrupt having visibility to the kernel) which fires off
> sampling on associated ADCs.
> 5) Up to N iio capture devices for all channels that support timing capture.
> Note there is also hardware encoder capture support in these which should be
> correctly handled as well.  This comes back to an ancient discussion on the
> TI ecap units which have similar capabilities (driver never went anywhere but
> I think that was because the author left TI).
>
> Certainly for the IIO devices these should no be bound up into one instance
> as you have done here.
>
> Anyhow, I fear that right now this discussion is missing the key ingredient
> that the hardware is horrendously variable in it's capabilities and really
> is 4-5 different types of hardware that just happen to share a few bits of
> their offsets in their register maps.

Hardware really share the same registers mapping that why I have wrote
one only driver
per framework. Only the configurations are different

>
> So after all that I'm almost more confused than I was at the start!
>
> Jonathan
>
>
>>> +                                              TIM2_TRGO,
>>> +                                              TIM4_TRGO,
>>> +                                              TIM3_TRGO;
>>> +                    st,output-triggers-names = TIM1_TRGO;
>>> +            };
>>> +    };
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>



-- 
Benjamin Gaignard

Graphic Study Group

Linaro.org ? Open source software for ARM SoCs

Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply

* [RFC PATCH 1/3] drm: Add support for Amlogic Meson Graphic Controller
From: Daniel Vetter @ 2016-11-29  8:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <99e71b1b-b0cc-ec54-6ca9-417d20195aca@baylibre.com>

Hi Neil,

On Mon, Nov 28, 2016 at 10:34:58AM +0100, Neil Armstrong wrote:
> On 11/28/2016 09:16 AM, Daniel Vetter wrote:
> > On Fri, Nov 25, 2016 at 05:03:09PM +0100, Neil Armstrong wrote:
> >> +static void meson_cvbs_encoder_disable(struct drm_encoder *encoder)
> >> +{
> >> +	struct meson_cvbs *meson_cvbs = encoder_to_meson_cvbs(encoder);
> >> +
> >> +	meson_venci_cvbs_disable(meson_cvbs->priv);
> >> +}
> >> +
> >> +static void meson_cvbs_encoder_enable(struct drm_encoder *encoder)
> >> +{
> >> +	struct meson_cvbs *meson_cvbs = encoder_to_meson_cvbs(encoder);
> >> +
> >> +	meson_venci_cvbs_enable(meson_cvbs->priv);
> >> +}
> > 
> > Personally I'd remove the indirection above, more direct code is easier to
> > read.
> 
> I understand, I'll maybe change the meson_venci_cvbs_XXable to be
> directly added to the ops.
> 
> I want to keep the registers setup in separate files and keep a clean
> DRM/HW separation.

I figured this is worth clarifying, and I'm somewhat guessing at your
motivation here for a clean drm/hw split. There's of course various levels
of how much you can split the drm side from your hw backend, but in
general that design approach is really unpopular with upstream. It goes by
the name of "midlayer", and the trouble with it is that it makes
subsystem refactoring more complicated.

For the driver itself it's nice, because it isolates you a bit from drm
core. But that exact isolation is the problem when someone wants (or more
often, needs to) refactor something across the entire subsystem. Then all
these driver-private little (or sometimes much bigger) abstractions get in
the way. That's way I suggested to remove it (both here and in the plane
code), because for upstream the overall subsystem matters more than each
individual driver. GPUs change fast, we need to be able to adapt fast,
too.

Anyway you're driver's pretty small, so personally I don't mind much. I'd
still think removing the indirection would be better though.

Thanks, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply

* [PATCH v2 1/6] mm: hugetlb: rename some allocation functions
From: Huang Shijie @ 2016-11-29  8:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52b661c9-f4b0-3d94-cf9b-a0ffd5ecb723@suse.cz>

On Mon, Nov 28, 2016 at 02:29:03PM +0100, Vlastimil Babka wrote:
> On 11/14/2016 08:07 AM, Huang Shijie wrote:
> >  static inline bool gigantic_page_supported(void) { return true; }
> >  #else
> > +static inline struct page *alloc_gigantic_page(int nid, unsigned int order)
> > +{
> > +	return NULL;
> > +}
> 
> This hunk is not explained by the description. Could belong to a later
> patch?
> 

Okay, I can create an extra patch to add the description for the
alloc_gigantic_page().

Thanks
Huang Shijie

^ permalink raw reply

* [PATCH V2 fix 5/6] mm: hugetlb: add a new function to allocate a new gigantic page
From: Huang Shijie @ 2016-11-29  9:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <f6fc93b4-5c1c-bbab-7c74-a0d60d4afc84@suse.cz>

On Mon, Nov 28, 2016 at 03:17:28PM +0100, Vlastimil Babka wrote:
> On 11/16/2016 07:55 AM, Huang Shijie wrote:
> > +static struct page *__hugetlb_alloc_gigantic_page(struct hstate *h,
> > +		struct vm_area_struct *vma, unsigned long addr, int nid)
> > +{
> > +	NODEMASK_ALLOC(nodemask_t, nodes_allowed, GFP_KERNEL | __GFP_NORETRY);
> 
> What if the allocation fails and nodes_allowed is NULL?
> It might work fine now, but it's rather fragile, so I'd rather see an
Yes.

> explicit check.
See the comment below.

> 
> BTW same thing applies to __nr_hugepages_store_common().
> 
> > +	struct page *page = NULL;
> > +
> > +	/* Not NUMA */
> > +	if (!IS_ENABLED(CONFIG_NUMA)) {
> > +		if (nid == NUMA_NO_NODE)
> > +			nid = numa_mem_id();
> > +
> > +		page = alloc_gigantic_page(nid, huge_page_order(h));
> > +		if (page)
> > +			prep_compound_gigantic_page(page, huge_page_order(h));
> > +
> > +		NODEMASK_FREE(nodes_allowed);
> > +		return page;
> > +	}
> > +
> > +	/* NUMA && !vma */
> > +	if (!vma) {
> > +		if (nid == NUMA_NO_NODE) {
> > +			if (!init_nodemask_of_mempolicy(nodes_allowed)) {
> > +				NODEMASK_FREE(nodes_allowed);
> > +				nodes_allowed = &node_states[N_MEMORY];
> > +			}
> > +		} else if (nodes_allowed) {
The check is here.

Do we really need to re-arrange the code here for the explicit check? :)


Thanks
Huang Shijie
> > +			init_nodemask_of_node(nodes_allowed, nid);
> > +		} else {
> > +			nodes_allowed = &node_states[N_MEMORY];
> > +		}
> > +
> > +		page = alloc_fresh_gigantic_page(h, nodes_allowed, true);
> > +

^ permalink raw reply

* [RFC PATCH 1/3] drm: Add support for Amlogic Meson Graphic Controller
From: Neil Armstrong @ 2016-11-29  9:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161129085024.tqf5takulvv7f34x@phenom.ffwll.local>

Hi Daniel,
On 11/29/2016 09:50 AM, Daniel Vetter wrote:
> Hi Neil,
> 
> On Mon, Nov 28, 2016 at 10:34:58AM +0100, Neil Armstrong wrote:
>> On 11/28/2016 09:16 AM, Daniel Vetter wrote:
>>> On Fri, Nov 25, 2016 at 05:03:09PM +0100, Neil Armstrong wrote:
>>>> +static void meson_cvbs_encoder_disable(struct drm_encoder *encoder)
>>>> +{
>>>> +	struct meson_cvbs *meson_cvbs = encoder_to_meson_cvbs(encoder);
>>>> +
>>>> +	meson_venci_cvbs_disable(meson_cvbs->priv);
>>>> +}
>>>> +
>>>> +static void meson_cvbs_encoder_enable(struct drm_encoder *encoder)
>>>> +{
>>>> +	struct meson_cvbs *meson_cvbs = encoder_to_meson_cvbs(encoder);
>>>> +
>>>> +	meson_venci_cvbs_enable(meson_cvbs->priv);
>>>> +}
>>>
>>> Personally I'd remove the indirection above, more direct code is easier to
>>> read.
>>
>> I understand, I'll maybe change the meson_venci_cvbs_XXable to be
>> directly added to the ops.
>>
>> I want to keep the registers setup in separate files and keep a clean
>> DRM/HW separation.
> 
> I figured this is worth clarifying, and I'm somewhat guessing at your
> motivation here for a clean drm/hw split. There's of course various levels
> of how much you can split the drm side from your hw backend, but in
> general that design approach is really unpopular with upstream. It goes by
> the name of "midlayer", and the trouble with it is that it makes
> subsystem refactoring more complicated.

I totally understand, and I personally would prefer to have an unified drm source,
but the state of the hardware architecture makes it very hard to map cleanly over DRM.
I moved all the CRTC and Plane code into the corresponding file ans only kept
the init and common functions in the backend files.

> 
> For the driver itself it's nice, because it isolates you a bit from drm
> core. But that exact isolation is the problem when someone wants (or more
> often, needs to) refactor something across the entire subsystem. Then all
> these driver-private little (or sometimes much bigger) abstractions get in
> the way. That's way I suggested to remove it (both here and in the plane
> code), because for upstream the overall subsystem matters more than each
> individual driver. GPUs change fast, we need to be able to adapt fast,
> too.

It totally makes sense and I'm totally ready to refactor when necessary and match
the DRM/KMS architecture.

> 
> Anyway you're driver's pretty small, so personally I don't mind much. I'd
> still think removing the indirection would be better though.
> 
> Thanks, Daniel
> 

Thanks,
Neil

^ permalink raw reply

* [PATCH v2 0/6] mm: fix the "counter.sh" failure for libhugetlbfs
From: Huang Shijie @ 2016-11-29  9:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <6b83ea5d-a465-7582-a215-51a21fb4ce2e@suse.cz>

On Mon, Nov 28, 2016 at 03:20:05PM +0100, Vlastimil Babka wrote:
> > Huang Shijie (6):
> >   mm: hugetlb: rename some allocation functions
> >   mm: hugetlb: add a new parameter for some functions
> >   mm: hugetlb: change the return type for alloc_fresh_gigantic_page
> >   mm: mempolicy: intruduce a helper huge_nodemask()
> >   mm: hugetlb: add a new function to allocate a new gigantic page
> >   mm: hugetlb: support gigantic surplus pages
> > 
> >  include/linux/mempolicy.h |   8 +++
> >  mm/hugetlb.c              | 128 ++++++++++++++++++++++++++++++++++++----------
> >  mm/mempolicy.c            |  20 ++++++++
> >  3 files changed, 130 insertions(+), 26 deletions(-)
> 
> Can't say I'm entirely happy with the continued direction of maze of
> functions for huge page allocation :( Feels like path of least resistance to
> basically copy/paste the missing parts here. Is there no way to consolidate
> the code more?
Ok, I will spend some time to read the code and think about it.

If you have interest, please do it too. :)

Thanks
Huang Shijie

^ permalink raw reply

* [PATCH v7 4/8] drm/sunxi: Add DT bindings documentation of Allwinner HDMI
From: Jean-Francois Moine @ 2016-11-29  9:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1480414715.git.moinejf@free.fr>

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 .../devicetree/bindings/display/sunxi/hdmi.txt     | 56 ++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt

diff --git a/Documentation/devicetree/bindings/display/sunxi/hdmi.txt b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
new file mode 100644
index 0000000..1e107cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
@@ -0,0 +1,56 @@
+Allwinner HDMI Transmitter
+==========================
+
+The Allwinner HDMI transmitters are included in the SoCs.
+They support audio and video.
+
+Required properties:
+ - compatible : should be one of
+		"allwinner,sun8i-a83t-hdmi"
+		"allwinner,sun8i-h3-hdmi"
+ - reg: base address and size of the I/O memory
+ - clocks : phandles to the HDMI clocks as described in
+	Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names : must be
+		"bus" : bus gate
+		"clock" : streaming clock
+		"ddc-clock" : DDC clock
+ - resets : One or two phandles to the HDMI resets
+ - reset-names : when 2 phandles, must be
+		"hdmi0" and "hdmi1"
+ - #address-cells : should be <1>
+ - #size-cells : should be <0>
+
+Required nodes:
+ - port: Audio and video input port nodes with endpoint definitions
+	as defined in Documentation/devicetree/bindings/graph.txt.
+	port at 0 is video and port at 1 is audio.
+
+Example:
+
+	hdmi: hdmi at 01ee0000 {
+		compatible = "allwinner,sun8i-a83t-hdmi";
+		reg = <0x01ee0000 0x20000>;
+		clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI>,
+			 <&ccu CLK_HDMI_DDC>;
+		clock-names = "bus", "clock", "ddc-clock";
+		resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
+		reset-names = "hdmi0", "hdmi1";
+		pinctrl-names = "default";
+		pinctrl-0 = <&hdmi_pins_a>;
+		status = "disabled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		port at 0 {			/* video */
+			reg = <0>;
+			hdmi_tcon1: endpoint {
+				remote-endpoint = <&tcon1_hdmi>;
+			};
+		};
+		port at 1 {			/* audio */
+			reg = <1>;
+			hdmi_i2s2: endpoint {
+				remote-endpoint = <&i2s2_hdmi>;
+			};
+		};
+	};
-- 
2.10.2

^ permalink raw reply related

* [PATCH] arm64: dts: Add symlinks for cros-ec-keyboard and cros-ec-sbs
From: Heiko Stübner @ 2016-11-29  9:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161128235132.GA71295@google.com>

Am Montag, 28. November 2016, 15:51:35 schrieb Brian Norris:
> Hi Olof, Arnd,
> 
> On Thu, May 12, 2016 at 03:02:10PM -0700, Doug Anderson wrote:
> > We'd like to be able to use the cros-ec-keyboard.dtsi and
> > cros-ec-sbs.dtsi snippets for arm64 devices.  Currently those files live
> > in the arm/boot/dts directory.
> > 
> > Let's follow the convention set by commit 8ee57b8182c4 ("ARM64: dts:
> > vexpress: Use a symlink to vexpress-v2m-rs1.dtsi from arch=arm") and use
> > a symlink.  Note that in this case we put the files in a new
> > "include/common" directory since these snippets may need to be
> > referenced by dts files in many different subdirectories.
> > 
> > Signed-off-by: Douglas Anderson <dianders@chromium.org>
> > ---
> > Note that, as of right now, there are no users of this.  However, given
> > development happening it is almost 100% certain that users will arrive
> > soon.  If we need to wait for the first user before landing this we can
> > leave this on the back burner.  If it's OK to have no users (yet), let's
> > land.
> 
> This may not fit your definition of "soon", but I'm looking to start
> using these files in arch/arm64/. I'll probably carry along this patch
> and resend when I'm ready, but it'd be just as well if you'd merge it
> now (or express a preference for a different directory structure).

Till now I was expecting to just pick up this patch as well once the first 
(rk3399-)users arrived. But of course it could also be picked up separately.


> >  arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi | 1 +
> >  arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi      | 1 +
> >  2 files changed, 2 insertions(+)
> >  create mode 120000
> >  arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
> >  create mode 120000 arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
> > 
> > \ No newline at end of file
> > 
> > diff --git a/arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
> > b/arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi new file mode
> > 120000
> > index 000000000000..1c1889f0a791
> > --- /dev/null
> > +++ b/arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
> > @@ -0,0 +1 @@
> > +../../../../../arm/boot/dts/cros-ec-keyboard.dtsi
> > \ No newline at end of file
> > diff --git a/arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
> > b/arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi new file mode
> > 120000
> > index 000000000000..3d7ae9c88bcd
> > --- /dev/null
> > +++ b/arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
> > @@ -0,0 +1 @@
> > +../../../../../arm/boot/dts/cros-ec-sbs.dtsi
> 
> FWIW:
> 
> Reviewed-by: Brian Norris <briannorris@chromium.org>

I'm also running with this on my rk3399-gru and it sucessfully enables the 
cros-ec keyboard :-) ,so if anyone picks it up before me they can add

Reviewed-by: Heiko Stueber <heiko@sntech.de>
Tested-by: Heiko Stuebner <heiko@sntech.de>

^ permalink raw reply

* [PATCH v5 8/8] iommu/rockchip: Enable Rockchip IOMMU on ARM64
From: Heiko Stübner @ 2016-11-29  9:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161129004225.GA109697@google.com>

Am Montag, 28. November 2016, 16:42:27 schrieb Brian Norris:
> Hi,
> 
> On Fri, Jun 24, 2016 at 10:13:33AM +0800, Shunqian Zheng wrote:
> > From: Simon Xue <xxm@rock-chips.com>
> > 
> > This patch makes it possible to compile the rockchip-iommu driver on
> > ARM64, so that it can be used with 64-bit SoCs equipped with this type
> > of IOMMU.
> > 
> > Signed-off-by: Simon Xue <xxm@rock-chips.com>
> > Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> > Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> > ---
> > 
> >  drivers/iommu/Kconfig | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> > index ad08603..5572621 100644
> > --- a/drivers/iommu/Kconfig
> > +++ b/drivers/iommu/Kconfig
> > @@ -218,7 +218,7 @@ config OMAP_IOMMU_DEBUG
> > 
> >  config ROCKCHIP_IOMMU
> >  
> >  	bool "Rockchip IOMMU Support"
> > 
> > -	depends on ARM
> > +	depends on ARM || ARM64
> > 
> >  	depends on ARCH_ROCKCHIP || COMPILE_TEST
> >  	select IOMMU_API
> >  	select ARM_DMA_USE_IOMMU
> 
> Whatever happened with the rest of this series? Some of the IOMMU bits
> made it, but the DRM fixes never did, and so this didn't get applied.
> This leaves the whole DRM stack unusable on ARM64 Rockchip systems.
> 
> The patch context has changed a bit on patch 7 (and maybe 6?), so
> somebody will need to refresh those on the latest upstream.

I've tried forward-porting these when going through Caesars VOP dts patches, 
but was somewhat unsucessful in my small attempt at the time - see [0].


Heiko

[0] https://lkml.org/lkml/2016/11/14/519

^ permalink raw reply

* [PATCH v2] drm: tilcdc: fix parsing of some DT properties
From: Jyri Sarha @ 2016-11-29  9:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1480333043-1292-1-git-send-email-bgolaszewski@baylibre.com>

On 11/28/16 13:37, Bartosz Golaszewski wrote:
> The DT binding for tildc is not consistent with the driver code: there
> are two options - 'max-width' and 'max-pixelclock' specified in the
> documentation which are parsed as 'ti,max-width' and
> 'ti'max-pixelclock' respectively.
> 
> Make the driver code consistent with the binding.
> 
> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Reviewed-by: Jyri Sarha <jsarha@ti.com>

I'll pick this up for a pull request that I'll send shortly.

> ---
> v1 -> v2:
> - fix max-pixelclock too
> 
>  drivers/gpu/drm/tilcdc/tilcdc_drv.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
> index 5efb369..bd0a3bd 100644
> --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c
> +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
> @@ -296,12 +296,12 @@ static int tilcdc_init(struct drm_driver *ddrv, struct device *dev)
>  
>  	DBG("Maximum Bandwidth Value %d", priv->max_bandwidth);
>  
> -	if (of_property_read_u32(node, "ti,max-width", &priv->max_width))
> +	if (of_property_read_u32(node, "max-width", &priv->max_width))
>  		priv->max_width = TILCDC_DEFAULT_MAX_WIDTH;
>  
>  	DBG("Maximum Horizontal Pixel Width Value %dpixels", priv->max_width);
>  
> -	if (of_property_read_u32(node, "ti,max-pixelclock",
> +	if (of_property_read_u32(node, "max-pixelclock",
>  					&priv->max_pixelclock))
>  		priv->max_pixelclock = TILCDC_DEFAULT_MAX_PIXELCLOCK;
>  
> 

^ permalink raw reply

* [PATCH 0/2] ARM: dts: dra7-evm: pinmuxing updates
From: Sekhar Nori @ 2016-11-29  9:24 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

This patch series drops most of the pinmuxing from dra7-evm
to satisfy erratum i869 and adds missing eMMC and SD card pinmuxing.

The patches apply to latest linux-next. Tested on DRA74x EVM Rev H
using U-Boot 2016.11. Boot tested using NFS and basic testing carried
out on SD card, eMMC, Audio capture and playback.

Kishon Vijay Abraham I (1):
  ARM: dts: dra7-evm: add pinmux configuration for mmc1/2

Sekhar Nori (1):
  ARM: dts: dra7-evm: Remove pinmux configurations for erratum i869

 arch/arm/boot/dts/dra7-evm.dts | 275 ++++-------------------------------------
 1 file changed, 23 insertions(+), 252 deletions(-)

-- 
2.9.0

^ permalink raw reply

* [PATCH 1/2] ARM: dts: dra7-evm: Remove pinmux configurations for erratum i869
From: Sekhar Nori @ 2016-11-29  9:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1480411308.git.nsekhar@ti.com>

Pinmuxing for DRA7x/AM57x family of processors need to be done in IO
isolation as part of initial bootloader executed from SRAM. This is
done as part of iodelay configuration sequence and is required due
to the limitations introduced by erratum ID: i869[1] (IO Glitches
can occur when changing IO settings) and elaborated in the Technical
Reference Manual[2] 18.4.6.1.7 Isolation Requirements.

Only peripheral that is permitted for dynamic pin mux configuration
is MMC and DCAN. MMC is permitted to change to accommodate the
requirements for varied speeds (which require IO-delay support in
kernel as well). DCAN is a result of i893[1] (DCAN initialization
sequence).

DCAN pinmux is retained in this patch. MMC pinmux is missing from
the dra7-evm.dts file and the board is relying on configuration done
by bootloader. A subsequent patch will add MMC pinmux configuration.

A side-effect of this patch is that NAND support is removed. NAND
pins clash with VOUT3 on DRA7-EVM. U-Boot selects VOUT3 over NAND
as per TI EVM application needs.

[1] http://www.ti.com/lit/pdf/sprz429
[2] http://www.ti.com/lit/pdf/sprui30

Signed-off-by: Sekhar Nori <nsekhar@ti.com>
---
 arch/arm/boot/dts/dra7-evm.dts | 260 -----------------------------------------
 1 file changed, 260 deletions(-)

diff --git a/arch/arm/boot/dts/dra7-evm.dts b/arch/arm/boot/dts/dra7-evm.dts
index 132f2be10889..a1efd4efed6d 100644
--- a/arch/arm/boot/dts/dra7-evm.dts
+++ b/arch/arm/boot/dts/dra7-evm.dts
@@ -151,204 +151,6 @@
 };
 
 &dra7_pmx_core {
-	pinctrl-names = "default";
-	pinctrl-0 = <&vtt_pin>;
-
-	vtt_pin: pinmux_vtt_pin {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x37b4, PIN_OUTPUT | MUX_MODE14) /* spi1_cs1.gpio7_11 */
-		>;
-	};
-
-	i2c1_pins: pinmux_i2c1_pins {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x3800, PIN_INPUT | MUX_MODE0) /* i2c1_sda */
-			DRA7XX_CORE_IOPAD(0x3804, PIN_INPUT | MUX_MODE0) /* i2c1_scl */
-		>;
-	};
-
-	i2c2_pins: pinmux_i2c2_pins {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x3808, PIN_INPUT | MUX_MODE0) /* i2c2_sda */
-			DRA7XX_CORE_IOPAD(0x380c, PIN_INPUT | MUX_MODE0) /* i2c2_scl */
-		>;
-	};
-
-	i2c3_pins: pinmux_i2c3_pins {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x3688, PIN_INPUT | MUX_MODE9) /* gpio6_14.i2c3_sda */
-			DRA7XX_CORE_IOPAD(0x368c, PIN_INPUT | MUX_MODE9) /* gpio6_15.i2c3_scl */
-		>;
-	};
-
-	mcspi1_pins: pinmux_mcspi1_pins {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x37a4, PIN_INPUT | MUX_MODE0) /* spi1_sclk */
-			DRA7XX_CORE_IOPAD(0x37a8, PIN_INPUT | MUX_MODE0) /* spi1_d1 */
-			DRA7XX_CORE_IOPAD(0x37ac, PIN_INPUT | MUX_MODE0) /* spi1_d0 */
-			DRA7XX_CORE_IOPAD(0x37b0, PIN_INPUT_SLEW | MUX_MODE0) /* spi1_cs0 */
-			DRA7XX_CORE_IOPAD(0x37b8, PIN_INPUT_SLEW | MUX_MODE6) /* spi1_cs2.hdmi1_hpd */
-			DRA7XX_CORE_IOPAD(0x37bc, PIN_INPUT_SLEW | MUX_MODE6) /* spi1_cs3.hdmi1_cec */
-		>;
-	};
-
-	mcspi2_pins: pinmux_mcspi2_pins {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x37c0, PIN_INPUT | MUX_MODE0) /* spi2_sclk */
-			DRA7XX_CORE_IOPAD(0x37c4, PIN_INPUT_SLEW | MUX_MODE0) /* spi2_d1 */
-			DRA7XX_CORE_IOPAD(0x37c8, PIN_INPUT_SLEW | MUX_MODE0) /* spi2_d1 */
-			DRA7XX_CORE_IOPAD(0x37cc, PIN_INPUT_SLEW | MUX_MODE0) /* spi2_cs0 */
-		>;
-	};
-
-	uart1_pins: pinmux_uart1_pins {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x37e0, PIN_INPUT_SLEW | MUX_MODE0) /* uart1_rxd */
-			DRA7XX_CORE_IOPAD(0x37e4, PIN_INPUT_SLEW | MUX_MODE0) /* uart1_txd */
-			DRA7XX_CORE_IOPAD(0x37e8, PIN_INPUT | MUX_MODE3) /* uart1_ctsn */
-			DRA7XX_CORE_IOPAD(0x37ec, PIN_INPUT | MUX_MODE3) /* uart1_rtsn */
-		>;
-	};
-
-	uart2_pins: pinmux_uart2_pins {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x37f0, PIN_INPUT | MUX_MODE0) /* uart2_rxd */
-			DRA7XX_CORE_IOPAD(0x37f4, PIN_INPUT | MUX_MODE0) /* uart2_txd */
-			DRA7XX_CORE_IOPAD(0x37f8, PIN_INPUT | MUX_MODE0) /* uart2_ctsn */
-			DRA7XX_CORE_IOPAD(0x37fc, PIN_INPUT | MUX_MODE0) /* uart2_rtsn */
-		>;
-	};
-
-	uart3_pins: pinmux_uart3_pins {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x3648, PIN_INPUT_SLEW | MUX_MODE0) /* uart3_rxd */
-			DRA7XX_CORE_IOPAD(0x364c, PIN_INPUT_SLEW | MUX_MODE0) /* uart3_txd */
-		>;
-	};
-
-	usb1_pins: pinmux_usb1_pins {
-                pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x3680, PIN_INPUT_SLEW | MUX_MODE0) /* usb1_drvvbus */
-                >;
-        };
-
-	usb2_pins: pinmux_usb2_pins {
-                pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x3684, PIN_INPUT_SLEW | MUX_MODE0) /* usb2_drvvbus */
-                >;
-        };
-
-	nand_flash_x16: nand_flash_x16 {
-		/* On DRA7 EVM, GPMC_WPN and NAND_BOOTn comes from DIP switch
-		 * So NAND flash requires following switch settings:
-		 * SW5.1 (NAND_BOOTn) = ON (LOW)
-		 * SW5.9 (GPMC_WPN) = OFF (HIGH)
-		 */
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x3400, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad0	*/
-			DRA7XX_CORE_IOPAD(0x3404, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad1	*/
-			DRA7XX_CORE_IOPAD(0x3408, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad2	*/
-			DRA7XX_CORE_IOPAD(0x340c, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad3	*/
-			DRA7XX_CORE_IOPAD(0x3410, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad4	*/
-			DRA7XX_CORE_IOPAD(0x3414, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad5	*/
-			DRA7XX_CORE_IOPAD(0x3418, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad6	*/
-			DRA7XX_CORE_IOPAD(0x341c, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad7	*/
-			DRA7XX_CORE_IOPAD(0x3420, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad8	*/
-			DRA7XX_CORE_IOPAD(0x3424, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad9	*/
-			DRA7XX_CORE_IOPAD(0x3428, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad10	*/
-			DRA7XX_CORE_IOPAD(0x342c, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad11	*/
-			DRA7XX_CORE_IOPAD(0x3430, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad12	*/
-			DRA7XX_CORE_IOPAD(0x3434, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad13	*/
-			DRA7XX_CORE_IOPAD(0x3438, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad14	*/
-			DRA7XX_CORE_IOPAD(0x343c, PIN_INPUT  | MUX_MODE0)	/* gpmc_ad15	*/
-			DRA7XX_CORE_IOPAD(0x34d8, PIN_INPUT_PULLUP  | MUX_MODE0)	/* gpmc_wait0	*/
-			DRA7XX_CORE_IOPAD(0x34cc, PIN_OUTPUT | MUX_MODE0)	/* gpmc_wen	*/
-			DRA7XX_CORE_IOPAD(0x34b4, PIN_OUTPUT_PULLUP | MUX_MODE0)	/* gpmc_csn0	*/
-			DRA7XX_CORE_IOPAD(0x34c4, PIN_OUTPUT | MUX_MODE0)	/* gpmc_advn_ale */
-			DRA7XX_CORE_IOPAD(0x34c8, PIN_OUTPUT | MUX_MODE0)	/* gpmc_oen_ren	 */
-			DRA7XX_CORE_IOPAD(0x34d0, PIN_OUTPUT | MUX_MODE0)	/* gpmc_be0n_cle */
-		>;
-	};
-
-	cpsw_default: cpsw_default {
-		pinctrl-single,pins = <
-			/* Slave 1 */
-			DRA7XX_CORE_IOPAD(0x3650, PIN_OUTPUT | MUX_MODE0)	/* rgmii0_txc.rgmii0_txc */
-			DRA7XX_CORE_IOPAD(0x3654, PIN_OUTPUT | MUX_MODE0)	/* rgmii0_txctl.rgmii0_txctl */
-			DRA7XX_CORE_IOPAD(0x3658, PIN_OUTPUT | MUX_MODE0)	/* rgmii0_td3.rgmii0_txd3 */
-			DRA7XX_CORE_IOPAD(0x365c, PIN_OUTPUT | MUX_MODE0)	/* rgmii0_txd2.rgmii0_txd2 */
-			DRA7XX_CORE_IOPAD(0x3660, PIN_OUTPUT | MUX_MODE0)	/* rgmii0_txd1.rgmii0_txd1 */
-			DRA7XX_CORE_IOPAD(0x3664, PIN_OUTPUT | MUX_MODE0)	/* rgmii0_txd0.rgmii0_txd0 */
-			DRA7XX_CORE_IOPAD(0x3668, PIN_INPUT | MUX_MODE0)	/* rgmii0_rxc.rgmii0_rxc */
-			DRA7XX_CORE_IOPAD(0x366c, PIN_INPUT | MUX_MODE0)	/* rgmii0_rxctl.rgmii0_rxctl */
-			DRA7XX_CORE_IOPAD(0x3670, PIN_INPUT | MUX_MODE0)	/* rgmii0_rxd3.rgmii0_rxd3 */
-			DRA7XX_CORE_IOPAD(0x3674, PIN_INPUT | MUX_MODE0)	/* rgmii0_rxd2.rgmii0_rxd2 */
-			DRA7XX_CORE_IOPAD(0x3678, PIN_INPUT | MUX_MODE0)	/* rgmii0_rxd1.rgmii0_rxd1 */
-			DRA7XX_CORE_IOPAD(0x367c, PIN_INPUT | MUX_MODE0)	/* rgmii0_rxd0.rgmii0_rxd0 */
-
-			/* Slave 2 */
-			DRA7XX_CORE_IOPAD(0x3598, PIN_OUTPUT | MUX_MODE3)	/* vin2a_d12.rgmii1_txc */
-			DRA7XX_CORE_IOPAD(0x359c, PIN_OUTPUT | MUX_MODE3)	/* vin2a_d13.rgmii1_tctl */
-			DRA7XX_CORE_IOPAD(0x35a0, PIN_OUTPUT | MUX_MODE3)	/* vin2a_d14.rgmii1_td3 */
-			DRA7XX_CORE_IOPAD(0x35a4, PIN_OUTPUT | MUX_MODE3)	/* vin2a_d15.rgmii1_td2 */
-			DRA7XX_CORE_IOPAD(0x35a8, PIN_OUTPUT | MUX_MODE3)	/* vin2a_d16.rgmii1_td1 */
-			DRA7XX_CORE_IOPAD(0x35ac, PIN_OUTPUT | MUX_MODE3)	/* vin2a_d17.rgmii1_td0 */
-			DRA7XX_CORE_IOPAD(0x35b0, PIN_INPUT | MUX_MODE3)	/* vin2a_d18.rgmii1_rclk */
-			DRA7XX_CORE_IOPAD(0x35b4, PIN_INPUT | MUX_MODE3)	/* vin2a_d19.rgmii1_rctl */
-			DRA7XX_CORE_IOPAD(0x35b8, PIN_INPUT | MUX_MODE3)	/* vin2a_d20.rgmii1_rd3 */
-			DRA7XX_CORE_IOPAD(0x35bc, PIN_INPUT | MUX_MODE3)	/* vin2a_d21.rgmii1_rd2 */
-			DRA7XX_CORE_IOPAD(0x35c0, PIN_INPUT | MUX_MODE3)	/* vin2a_d22.rgmii1_rd1 */
-			DRA7XX_CORE_IOPAD(0x35c4, PIN_INPUT | MUX_MODE3)	/* vin2a_d23.rgmii1_rd0 */
-		>;
-
-	};
-
-	cpsw_sleep: cpsw_sleep {
-		pinctrl-single,pins = <
-			/* Slave 1 */
-			DRA7XX_CORE_IOPAD(0x3650, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x3654, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x3658, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x365c, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x3660, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x3664, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x3668, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x366c, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x3670, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x3674, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x3678, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x367c, MUX_MODE15)
-
-			/* Slave 2 */
-			DRA7XX_CORE_IOPAD(0x3598, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x359c, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x35a0, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x35a4, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x35a8, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x35ac, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x35b0, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x35b4, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x35b8, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x35bc, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x35c0, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x35c4, MUX_MODE15)
-		>;
-	};
-
-	davinci_mdio_default: davinci_mdio_default {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x363c, PIN_OUTPUT_PULLUP | MUX_MODE0)	/* mdio_d.mdio_d */
-			DRA7XX_CORE_IOPAD(0x3640, PIN_INPUT_PULLUP | MUX_MODE0)	/* mdio_clk.mdio_clk */
-		>;
-	};
-
-	davinci_mdio_sleep: davinci_mdio_sleep {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x363c, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x3640, MUX_MODE15)
-		>;
-	};
-
 	dcan1_pins_default: dcan1_pins_default {
 		pinctrl-single,pins = <
 			DRA7XX_CORE_IOPAD(0x37d0, PIN_OUTPUT_PULLUP | MUX_MODE0) /* dcan1_tx */
@@ -362,37 +164,10 @@
 			DRA7XX_CORE_IOPAD(0x3818, MUX_MODE15 | PULL_UP)	/* wakeup0.off */
 		>;
 	};
-
-	atl_pins: pinmux_atl_pins {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x3698, PIN_OUTPUT | MUX_MODE5)	/* xref_clk1.atl_clk1 */
-			DRA7XX_CORE_IOPAD(0x369c, PIN_OUTPUT | MUX_MODE5)	/* xref_clk2.atl_clk2 */
-		>;
-	};
-
-	mcasp3_pins: pinmux_mcasp3_pins {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x3724, PIN_OUTPUT_PULLDOWN | MUX_MODE0)	/* mcasp3_aclkx */
-			DRA7XX_CORE_IOPAD(0x3728, PIN_OUTPUT_PULLDOWN | MUX_MODE0)	/* mcasp3_fsx */
-			DRA7XX_CORE_IOPAD(0x372c, PIN_OUTPUT_PULLDOWN | MUX_MODE0)	/* mcasp3_axr0 */
-			DRA7XX_CORE_IOPAD(0x3730, PIN_INPUT_PULLDOWN | MUX_MODE0)	/* mcasp3_axr1 */
-		>;
-	};
-
-	mcasp3_sleep_pins: pinmux_mcasp3_sleep_pins {
-		pinctrl-single,pins = <
-			DRA7XX_CORE_IOPAD(0x3724, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x3728, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x372c, MUX_MODE15)
-			DRA7XX_CORE_IOPAD(0x3730, MUX_MODE15)
-		>;
-	};
 };
 
 &i2c1 {
 	status = "okay";
-	pinctrl-names = "default";
-	pinctrl-0 = <&i2c1_pins>;
 	clock-frequency = <400000>;
 
 	tps659038: tps659038 at 58 {
@@ -581,8 +356,6 @@
 
 &i2c2 {
 	status = "okay";
-	pinctrl-names = "default";
-	pinctrl-0 = <&i2c2_pins>;
 	clock-frequency = <400000>;
 
 	pcf_hdmi: gpio at 26 {
@@ -602,41 +375,29 @@
 
 &i2c3 {
 	status = "okay";
-	pinctrl-names = "default";
-	pinctrl-0 = <&i2c3_pins>;
 	clock-frequency = <400000>;
 };
 
 &mcspi1 {
 	status = "okay";
-	pinctrl-names = "default";
-	pinctrl-0 = <&mcspi1_pins>;
 };
 
 &mcspi2 {
 	status = "okay";
-	pinctrl-names = "default";
-	pinctrl-0 = <&mcspi2_pins>;
 };
 
 &uart1 {
 	status = "okay";
-	pinctrl-names = "default";
-	pinctrl-0 = <&uart1_pins>;
 	interrupts-extended = <&crossbar_mpu GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>,
 			      <&dra7_pmx_core 0x3e0>;
 };
 
 &uart2 {
 	status = "okay";
-	pinctrl-names = "default";
-	pinctrl-0 = <&uart2_pins>;
 };
 
 &uart3 {
 	status = "okay";
-	pinctrl-names = "default";
-	pinctrl-0 = <&uart3_pins>;
 };
 
 &mmc1 {
@@ -732,14 +493,10 @@
 
 &usb1 {
 	dr_mode = "peripheral";
-	pinctrl-names = "default";
-	pinctrl-0 = <&usb1_pins>;
 };
 
 &usb2 {
 	dr_mode = "host";
-	pinctrl-names = "default";
-	pinctrl-0 = <&usb2_pins>;
 };
 
 &elm {
@@ -748,8 +505,6 @@
 
 &gpmc {
 	status = "okay";
-	pinctrl-names = "default";
-	pinctrl-0 = <&nand_flash_x16>;
 	ranges = <0 0 0x08000000 0x01000000>;	/* minimum GPMC partition = 16MB */
 	nand at 0,0 {
 		compatible = "ti,omap2-nand";
@@ -845,9 +600,6 @@
 
 &mac {
 	status = "okay";
-	pinctrl-names = "default", "sleep";
-	pinctrl-0 = <&cpsw_default>;
-	pinctrl-1 = <&cpsw_sleep>;
 	dual_emac;
 };
 
@@ -863,12 +615,6 @@
 	dual_emac_res_vlan = <2>;
 };
 
-&davinci_mdio {
-	pinctrl-names = "default", "sleep";
-	pinctrl-0 = <&davinci_mdio_default>;
-	pinctrl-1 = <&davinci_mdio_sleep>;
-};
-
 &dcan1 {
 	status = "ok";
 	pinctrl-names = "default", "sleep", "active";
@@ -878,9 +624,6 @@
 };
 
 &atl {
-	pinctrl-names = "default";
-	pinctrl-0 = <&atl_pins>;
-
 	assigned-clocks = <&abe_dpll_sys_clk_mux>,
 			  <&atl_gfclk_mux>,
 			  <&dpll_abe_ck>,
@@ -899,9 +642,6 @@
 
 &mcasp3 {
 	#sound-dai-cells = <0>;
-	pinctrl-names = "default", "sleep";
-	pinctrl-0 = <&mcasp3_pins>;
-	pinctrl-1 = <&mcasp3_sleep_pins>;
 
 	assigned-clocks = <&mcasp3_ahclkx_mux>;
 	assigned-clock-parents = <&atl_clkin2_ck>;
-- 
2.9.0

^ permalink raw reply related

* [PATCH 2/2] ARM: dts: dra7-evm: add pinmux configuration for mmc1/2
From: Sekhar Nori @ 2016-11-29  9:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1480411308.git.nsekhar@ti.com>

From: Kishon Vijay Abraham I <kishon@ti.com>

Add pinmux configuration for SD card slot and eMMC device
found on TI's DRA74x EVM.

Only the default modes are supported. For higher speed modes
(UHS and HS200) to function, we need full fledged IODelay support
in kernel. IODelay support is yet to be added.

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
[nsekhar at ti.com: rebase to mainline/master, use IOPAD() macro,
		 update commit message]
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
---
 arch/arm/boot/dts/dra7-evm.dts | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/arch/arm/boot/dts/dra7-evm.dts b/arch/arm/boot/dts/dra7-evm.dts
index a1efd4efed6d..1fc29ed636c9 100644
--- a/arch/arm/boot/dts/dra7-evm.dts
+++ b/arch/arm/boot/dts/dra7-evm.dts
@@ -164,6 +164,33 @@
 			DRA7XX_CORE_IOPAD(0x3818, MUX_MODE15 | PULL_UP)	/* wakeup0.off */
 		>;
 	};
+
+	mmc1_pins_default: mmc1_pins_default {
+		pinctrl-single,pins = <
+			DRA7XX_CORE_IOPAD(0x376c, PIN_INPUT | MUX_MODE14)	/* mmc1sdcd.gpio219 */
+			DRA7XX_CORE_IOPAD(0x3754, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc1_clk.clk */
+			DRA7XX_CORE_IOPAD(0x3758, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc1_cmd.cmd */
+			DRA7XX_CORE_IOPAD(0x375c, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc1_dat0.dat0 */
+			DRA7XX_CORE_IOPAD(0x3760, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc1_dat1.dat1 */
+			DRA7XX_CORE_IOPAD(0x3764, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc1_dat2.dat2 */
+			DRA7XX_CORE_IOPAD(0x3768, PIN_INPUT_PULLUP | MUX_MODE0) /* mmc1_dat3.dat3 */
+		>;
+	};
+
+	mmc2_pins_default: mmc2_pins_default {
+		pinctrl-single,pins = <
+			DRA7XX_CORE_IOPAD(0x349c, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_a23.mmc2_clk */
+			DRA7XX_CORE_IOPAD(0x34b0, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_cs1.mmc2_cmd */
+			DRA7XX_CORE_IOPAD(0x34a0, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_a24.mmc2_dat0 */
+			DRA7XX_CORE_IOPAD(0x34a4, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_a25.mmc2_dat1 */
+			DRA7XX_CORE_IOPAD(0x34a8, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_a26.mmc2_dat2 */
+			DRA7XX_CORE_IOPAD(0x34ac, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_a27.mmc2_dat3 */
+			DRA7XX_CORE_IOPAD(0x348c, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_a19.mmc2_dat4 */
+			DRA7XX_CORE_IOPAD(0x3490, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_a20.mmc2_dat5 */
+			DRA7XX_CORE_IOPAD(0x3494, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_a21.mmc2_dat6 */
+			DRA7XX_CORE_IOPAD(0x3498, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_a22.mmc2_dat7 */
+		>;
+	};
 };
 
 &i2c1 {
@@ -402,6 +429,8 @@
 
 &mmc1 {
 	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&mmc1_pins_default>;
 	vmmc-supply = <&evm_3v3_sd>;
 	vmmc_aux-supply = <&ldo1_reg>;
 	bus-width = <4>;
@@ -414,6 +443,8 @@
 
 &mmc2 {
 	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&mmc2_pins_default>;
 	vmmc-supply = <&evm_3v3_sw>;
 	bus-width = <8>;
 };
-- 
2.9.0

^ permalink raw reply related

* [PATCH] KVM: arm/arm64: Access CNTHCTL_EL2 bit fields correctly
From: Marc Zyngier @ 2016-11-29  9:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAHyh4xhXoFWsbnNNePthWp2-ZAOtCuUVmLn-JKRe4sc=nqrYNw@mail.gmail.com>

On 29/11/16 03:28, Jintack Lim wrote:
> On Mon, Nov 28, 2016 at 1:39 PM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> On 28/11/16 17:43, Marc Zyngier wrote:
>>> Hi Jintack,
> 
> Hi Marc,
> 
>>>
>>> On 28/11/16 16:46, Jintack Lim wrote:
>>>> Bit positions of CNTHCTL_EL2 are changing depending on HCR_EL2.E2H bit.
>>>> EL1PCEN and EL1PCTEN are 1st and 0th bits when E2H is not set, but they
>>>> are 11th and 10th bits respectively when E2H is set.  Current code is
>>>> unintentionally setting wrong bits to CNTHCTL_EL2 with E2H set, which
>>>> may allow guest OS to access physical timer. So, fix it.
>>>>
>>>> Signed-off-by: Jintack Lim <jintack@cs.columbia.edu>
>>>> ---
>>>>  arch/arm/include/asm/kvm_timer.h     | 33 +++++++++++++++++++
>>>>  arch/arm64/include/asm/kvm_timer.h   | 62 ++++++++++++++++++++++++++++++++++++
>>>>  include/clocksource/arm_arch_timer.h |  6 ++--
>>>>  virt/kvm/arm/hyp/timer-sr.c          |  8 ++---
>>>>  4 files changed, 103 insertions(+), 6 deletions(-)
>>>>  create mode 100644 arch/arm/include/asm/kvm_timer.h
>>>>  create mode 100644 arch/arm64/include/asm/kvm_timer.h
>>>>
>>
>> [...]
>>
>>> We could make it nicer (read "faster") by introducing a
>>> hyp_alternate_select construct that only returns a value instead
>>> of calling a function. I remember writing something like that
>>> at some point, and dropping it...
>>
>> So here's what this could look like (warning, wacky code ahead,
>> though I fixed a stupid bug that was present in the previous patch).
>> The generated code is quite nice (no branch, only an extra mov
>> instruction on the default path)... Of course, completely untested!
> 
> This looks much cleaner than my patch.
> While we are at it, is it worth to consider that we just need to set
> those bits once for VHE case, not for every world switch as an
> optimization?

Ah! That's a much better idea indeed! And we could stop messing with
cntvoff_el2 as well, as it doesn't need to be restored to zero on exit.
Could you try and respin something along those lines?

Thanks,

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

^ permalink raw reply

* [PATCH] KVM: arm/arm64: Access CNTHCTL_EL2 bit fields correctly
From: Marc Zyngier @ 2016-11-29  9:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161128194221.GG18170@cbox>

On 28/11/16 19:42, Christoffer Dall wrote:
> On Mon, Nov 28, 2016 at 06:39:04PM +0000, Marc Zyngier wrote:
>> On 28/11/16 17:43, Marc Zyngier wrote:
>>> Hi Jintack,
>>>
>>> On 28/11/16 16:46, Jintack Lim wrote:
>>>> Bit positions of CNTHCTL_EL2 are changing depending on HCR_EL2.E2H bit.
>>>> EL1PCEN and EL1PCTEN are 1st and 0th bits when E2H is not set, but they
>>>> are 11th and 10th bits respectively when E2H is set.  Current code is
>>>> unintentionally setting wrong bits to CNTHCTL_EL2 with E2H set, which
>>>> may allow guest OS to access physical timer. So, fix it.
>>>>
>>>> Signed-off-by: Jintack Lim <jintack@cs.columbia.edu>
>>>> ---
>>>>  arch/arm/include/asm/kvm_timer.h     | 33 +++++++++++++++++++
>>>>  arch/arm64/include/asm/kvm_timer.h   | 62 ++++++++++++++++++++++++++++++++++++
>>>>  include/clocksource/arm_arch_timer.h |  6 ++--
>>>>  virt/kvm/arm/hyp/timer-sr.c          |  8 ++---
>>>>  4 files changed, 103 insertions(+), 6 deletions(-)
>>>>  create mode 100644 arch/arm/include/asm/kvm_timer.h
>>>>  create mode 100644 arch/arm64/include/asm/kvm_timer.h
>>>>
>>
>> [...]
>>
>>> We could make it nicer (read "faster") by introducing a
>>> hyp_alternate_select construct that only returns a value instead
>>> of calling a function. I remember writing something like that
>>> at some point, and dropping it...
>>
>> So here's what this could look like (warning, wacky code ahead,
>> though I fixed a stupid bug that was present in the previous patch).
>> The generated code is quite nice (no branch, only an extra mov
>> instruction on the default path)... Of course, completely untested!
> 
> Isn't this all about determining which bitmask to use, statically, once,
> after the system has booted?
> 
> How about a good old fashioned static variable, or global struct like
> the global one we use for the VGIC, which sets the proper mit mask
> during kvm init, and the world-switch code just uses a variable?

We could indeed do that (I've been carried away with my tendency for
weird and wonderful hacks).

But as Jintack mentioned, there is a much better approach, which is to
do nothing at all on the VHE path (we can set the permission bits once
and for all). cntvoff_el2 also falls into the same category of things we
should be able to only restore and not bother resetting (as it doesn't
affect the EL2 virtual counter).

Thoughts?

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

^ permalink raw reply

* [PATCH v3 net-next 0/6] Support Armada 37xx SoC (ARMv8 64-bits) in mvneta driver
From: Gregory CLEMENT @ 2016-11-29  9:37 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

The Armada 37xx is a new ARMv8 SoC from Marvell using same network
controller as the older Armada 370/38x/XP SoCs. This series adapts the
driver in order to be able to use it on this new SoC. The main changes
are:

- 64-bits support: the first patches allow using the driver on a 64-bit
  architecture.

- MBUS support: the mbus configuration is different on Armada 37xx
  from the older SoCs.

- per cpu interrupt: Armada 37xx do not support per cpu interrupt for
  the NETA IP, the non-per-CPU behavior was added back.

The first item is solved by patches 1 to 3.
The 2 last items are solved by patch 4.
In patch 5 the dt support is added.

Beside Armada 37xx, the series have been tested on Armada XP and
Armada 38x (with Hardware Buffer Management and with Software Buffer
Managment).

Thanks,

Gregory

Gregory CLEMENT (4):
  net: mvneta: Optimize rx path for small frame
  net: mvneta: Use cacheable memory to store the rx buffer virtual address
  net: mvneta: Only disable mvneta_bm for 64-bits
  ARM64: dts: marvell: Add network support for Armada 3700

Marcin Wojtas (2):
  net: mvneta: Convert to be 64 bits compatible
  net: mvneta: Add network support for Armada 3700 SoC

 Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt |   7 +-
 arch/arm64/boot/dts/marvell/armada-3720-db.dts                    |  23 ++++-
 arch/arm64/boot/dts/marvell/armada-37xx.dtsi                      |  23 ++++-
 drivers/net/ethernet/marvell/Kconfig                              |  10 +-
 drivers/net/ethernet/marvell/mvneta.c                             | 400 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
 5 files changed, 361 insertions(+), 102 deletions(-)

base-commit: 436accebb53021ef7c63535f60bda410aa87c136
-- 
git-series 0.8.10

^ permalink raw reply

* [PATCH v3 net-next 1/6] net: mvneta: Optimize rx path for small frame
From: Gregory CLEMENT @ 2016-11-29  9:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.a09aeaf5ba62853d595153626b6121ef52a3e9b4.1480412215.git-series.gregory.clement@free-electrons.com>

For small frame reuse the phys_addr variable instead of accessing the
uncacheable value in the rx descriptor.

Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 drivers/net/ethernet/marvell/mvneta.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 87274d4ab102..1b84f746d748 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -1918,7 +1918,7 @@ static int mvneta_rx_swbm(struct mvneta_port *pp, int rx_todo,
 				goto err_drop_frame;
 
 			dma_sync_single_range_for_cpu(dev->dev.parent,
-						      rx_desc->buf_phys_addr,
+						      phys_addr,
 						      MVNETA_MH_SIZE + NET_SKB_PAD,
 						      rx_bytes,
 						      DMA_FROM_DEVICE);
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v3 net-next 2/6] net: mvneta: Use cacheable memory to store the rx buffer virtual address
From: Gregory CLEMENT @ 2016-11-29  9:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.a09aeaf5ba62853d595153626b6121ef52a3e9b4.1480412215.git-series.gregory.clement@free-electrons.com>

Until now the virtual address of the received buffer were stored in the
cookie field of the rx descriptor. However, this field is 32-bits only
which prevents to use the driver on a 64-bits architecture.

With this patch the virtual address is stored in an array not shared with
the hardware (no more need to use the DMA API). Thanks to this, it is
possible to use cache contrary to the access of the rx descriptor member.

The change is done in the swbm path only because the hwbm uses the cookie
field, this also means that currently the hwbm is not usable in 64-bits.

Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 drivers/net/ethernet/marvell/mvneta.c | 93 ++++++++++++++++++++++++----
 1 file changed, 81 insertions(+), 12 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 1b84f746d748..32b142d0e44e 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -561,6 +561,9 @@ struct mvneta_rx_queue {
 	u32 pkts_coal;
 	u32 time_coal;
 
+	/* Virtual address of the RX buffer */
+	void  **buf_virt_addr;
+
 	/* Virtual address of the RX DMA descriptors array */
 	struct mvneta_rx_desc *descs;
 
@@ -1573,10 +1576,14 @@ static void mvneta_tx_done_pkts_coal_set(struct mvneta_port *pp,
 
 /* Handle rx descriptor fill by setting buf_cookie and buf_phys_addr */
 static void mvneta_rx_desc_fill(struct mvneta_rx_desc *rx_desc,
-				u32 phys_addr, u32 cookie)
+				u32 phys_addr, void *virt_addr,
+				struct mvneta_rx_queue *rxq)
 {
-	rx_desc->buf_cookie = cookie;
+	int i;
+
 	rx_desc->buf_phys_addr = phys_addr;
+	i = rx_desc - rxq->descs;
+	rxq->buf_virt_addr[i] = virt_addr;
 }
 
 /* Decrement sent descriptors counter */
@@ -1781,7 +1788,8 @@ EXPORT_SYMBOL_GPL(mvneta_frag_free);
 
 /* Refill processing for SW buffer management */
 static int mvneta_rx_refill(struct mvneta_port *pp,
-			    struct mvneta_rx_desc *rx_desc)
+			    struct mvneta_rx_desc *rx_desc,
+			    struct mvneta_rx_queue *rxq)
 
 {
 	dma_addr_t phys_addr;
@@ -1799,7 +1807,7 @@ static int mvneta_rx_refill(struct mvneta_port *pp,
 		return -ENOMEM;
 	}
 
-	mvneta_rx_desc_fill(rx_desc, phys_addr, (u32)data);
+	mvneta_rx_desc_fill(rx_desc, phys_addr, data, rxq);
 	return 0;
 }
 
@@ -1861,7 +1869,12 @@ static void mvneta_rxq_drop_pkts(struct mvneta_port *pp,
 
 	for (i = 0; i < rxq->size; i++) {
 		struct mvneta_rx_desc *rx_desc = rxq->descs + i;
-		void *data = (void *)rx_desc->buf_cookie;
+		void *data;
+
+		if (!pp->bm_priv)
+			data = rxq->buf_virt_addr[i];
+		else
+			data = (void *)(uintptr_t)rx_desc->buf_cookie;
 
 		dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr,
 				 MVNETA_RX_BUF_SIZE(pp->pkt_size), DMA_FROM_DEVICE);
@@ -1894,12 +1907,13 @@ static int mvneta_rx_swbm(struct mvneta_port *pp, int rx_todo,
 		unsigned char *data;
 		dma_addr_t phys_addr;
 		u32 rx_status, frag_size;
-		int rx_bytes, err;
+		int rx_bytes, err, index;
 
 		rx_done++;
 		rx_status = rx_desc->status;
 		rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE);
-		data = (unsigned char *)rx_desc->buf_cookie;
+		index = rx_desc - rxq->descs;
+		data = (unsigned char *)rxq->buf_virt_addr[index];
 		phys_addr = rx_desc->buf_phys_addr;
 
 		if (!mvneta_rxq_desc_is_first_last(rx_status) ||
@@ -1938,7 +1952,7 @@ static int mvneta_rx_swbm(struct mvneta_port *pp, int rx_todo,
 		}
 
 		/* Refill processing */
-		err = mvneta_rx_refill(pp, rx_desc);
+		err = mvneta_rx_refill(pp, rx_desc, rxq);
 		if (err) {
 			netdev_err(dev, "Linux processing - Can't refill\n");
 			rxq->missed++;
@@ -2020,7 +2034,7 @@ static int mvneta_rx_hwbm(struct mvneta_port *pp, int rx_todo,
 		rx_done++;
 		rx_status = rx_desc->status;
 		rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE);
-		data = (unsigned char *)rx_desc->buf_cookie;
+		data = (u8 *)(uintptr_t)rx_desc->buf_cookie;
 		phys_addr = rx_desc->buf_phys_addr;
 		pool_id = MVNETA_RX_GET_BM_POOL_ID(rx_desc);
 		bm_pool = &pp->bm_priv->bm_pools[pool_id];
@@ -2708,6 +2722,56 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
 	return rx_done;
 }
 
+/* Refill processing for HW buffer management */
+static int mvneta_rx_hwbm_refill(struct mvneta_port *pp,
+				 struct mvneta_rx_desc *rx_desc)
+
+{
+	dma_addr_t phys_addr;
+	void *data;
+
+	data = mvneta_frag_alloc(pp->frag_size);
+	if (!data)
+		return -ENOMEM;
+
+	phys_addr = dma_map_single(pp->dev->dev.parent, data,
+				   MVNETA_RX_BUF_SIZE(pp->pkt_size),
+				   DMA_FROM_DEVICE);
+	if (unlikely(dma_mapping_error(pp->dev->dev.parent, phys_addr))) {
+		mvneta_frag_free(pp->frag_size, data);
+		return -ENOMEM;
+	}
+
+	rx_desc->buf_phys_addr = phys_addr;
+	rx_desc->buf_cookie = (uintptr_t)data;
+
+	return 0;
+}
+
+/* Handle rxq fill: allocates rxq skbs; called when initializing a port */
+static int mvneta_rxq_bm_fill(struct mvneta_port *pp,
+			      struct mvneta_rx_queue *rxq,
+			      int num)
+{
+	int i;
+
+	for (i = 0; i < num; i++) {
+		memset(rxq->descs + i, 0, sizeof(struct mvneta_rx_desc));
+		if (mvneta_rx_hwbm_refill(pp, rxq->descs + i) != 0) {
+			netdev_err(pp->dev, "%s:rxq %d, %d of %d buffs  filled\n",
+				   __func__, rxq->id, i, num);
+			break;
+		}
+	}
+
+	/* Add this number of RX descriptors as non occupied (ready to
+	 * get packets)
+	 */
+	mvneta_rxq_non_occup_desc_add(pp, rxq, i);
+
+	return i;
+}
+
 /* Handle rxq fill: allocates rxq skbs; called when initializing a port */
 static int mvneta_rxq_fill(struct mvneta_port *pp, struct mvneta_rx_queue *rxq,
 			   int num)
@@ -2716,7 +2780,7 @@ static int mvneta_rxq_fill(struct mvneta_port *pp, struct mvneta_rx_queue *rxq,
 
 	for (i = 0; i < num; i++) {
 		memset(rxq->descs + i, 0, sizeof(struct mvneta_rx_desc));
-		if (mvneta_rx_refill(pp, rxq->descs + i) != 0) {
+		if (mvneta_rx_refill(pp, rxq->descs + i, rxq) != 0) {
 			netdev_err(pp->dev, "%s:rxq %d, %d of %d buffs  filled\n",
 				__func__, rxq->id, i, num);
 			break;
@@ -2784,14 +2848,14 @@ static int mvneta_rxq_init(struct mvneta_port *pp,
 		mvneta_rxq_buf_size_set(pp, rxq,
 					MVNETA_RX_BUF_SIZE(pp->pkt_size));
 		mvneta_rxq_bm_disable(pp, rxq);
+		mvneta_rxq_fill(pp, rxq, rxq->size);
 	} else {
 		mvneta_rxq_bm_enable(pp, rxq);
 		mvneta_rxq_long_pool_set(pp, rxq);
 		mvneta_rxq_short_pool_set(pp, rxq);
+		mvneta_rxq_bm_fill(pp, rxq, rxq->size);
 	}
 
-	mvneta_rxq_fill(pp, rxq, rxq->size);
-
 	return 0;
 }
 
@@ -3865,6 +3929,11 @@ static int mvneta_init(struct device *dev, struct mvneta_port *pp)
 		rxq->size = pp->rx_ring_size;
 		rxq->pkts_coal = MVNETA_RX_COAL_PKTS;
 		rxq->time_coal = MVNETA_RX_COAL_USEC;
+		rxq->buf_virt_addr = devm_kmalloc(pp->dev->dev.parent,
+						  rxq->size * sizeof(void *),
+						  GFP_KERNEL);
+		if (!rxq->buf_virt_addr)
+			return -ENOMEM;
 	}
 
 	return 0;
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v3 net-next 3/6] net: mvneta: Convert to be 64 bits compatible
From: Gregory CLEMENT @ 2016-11-29  9:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.a09aeaf5ba62853d595153626b6121ef52a3e9b4.1480412215.git-series.gregory.clement@free-electrons.com>

From: Marcin Wojtas <mw@semihalf.com>

Prepare the mvneta driver in order to be usable on the 64 bits platform
such as the Armada 3700.

[gregory.clement at free-electrons.com]: this patch was extract from a larger
one to ease review and maintenance.

Signed-off-by: Marcin Wojtas <mw@semihalf.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 drivers/net/ethernet/marvell/mvneta.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 32b142d0e44e..a8bd0d83028f 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -296,6 +296,12 @@
 /* descriptor aligned size */
 #define MVNETA_DESC_ALIGNED_SIZE	32
 
+/* Number of bytes to be taken into account by HW when putting incoming data
+ * to the buffers. It is needed in case NET_SKB_PAD exceeds maximum packet
+ * offset supported in MVNETA_RXQ_CONFIG_REG(q) registers.
+ */
+#define MVNETA_RX_PKT_OFFSET_CORRECTION		64
+
 #define MVNETA_RX_PKT_SIZE(mtu) \
 	ALIGN((mtu) + MVNETA_MH_SIZE + MVNETA_VLAN_TAG_LEN + \
 	      ETH_HLEN + ETH_FCS_LEN,			     \
@@ -416,6 +422,7 @@ struct mvneta_port {
 	u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
 
 	u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
+	u16 rx_offset_correction;
 };
 
 /* The mvneta_tx_desc and mvneta_rx_desc structures describe the
@@ -1807,6 +1814,7 @@ static int mvneta_rx_refill(struct mvneta_port *pp,
 		return -ENOMEM;
 	}
 
+	phys_addr += pp->rx_offset_correction;
 	mvneta_rx_desc_fill(rx_desc, phys_addr, data, rxq);
 	return 0;
 }
@@ -2742,6 +2750,7 @@ static int mvneta_rx_hwbm_refill(struct mvneta_port *pp,
 		return -ENOMEM;
 	}
 
+	phys_addr += pp->rx_offset_correction;
 	rx_desc->buf_phys_addr = phys_addr;
 	rx_desc->buf_cookie = (uintptr_t)data;
 
@@ -2837,7 +2846,7 @@ static int mvneta_rxq_init(struct mvneta_port *pp,
 	mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), rxq->size);
 
 	/* Set Offset */
-	mvneta_rxq_offset_set(pp, rxq, NET_SKB_PAD);
+	mvneta_rxq_offset_set(pp, rxq, NET_SKB_PAD - pp->rx_offset_correction);
 
 	/* Set coalescing pkts and time */
 	mvneta_rx_pkts_coal_set(pp, rxq, rxq->pkts_coal);
@@ -4088,6 +4097,13 @@ static int mvneta_probe(struct platform_device *pdev)
 
 	pp->rxq_def = rxq_def;
 
+	/* Set RX packet offset correction for platforms, whose
+	 * NET_SKB_PAD, exceeds 64B. It should be 64B for 64-bit
+	 * platforms and 0B for 32-bit ones.
+	 */
+	pp->rx_offset_correction =
+		max(0, NET_SKB_PAD - MVNETA_RX_PKT_OFFSET_CORRECTION);
+
 	pp->indir[0] = rxq_def;
 
 	pp->clk = devm_clk_get(&pdev->dev, "core");
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v3 net-next 4/6] net: mvneta: Only disable mvneta_bm for 64-bits
From: Gregory CLEMENT @ 2016-11-29  9:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.a09aeaf5ba62853d595153626b6121ef52a3e9b4.1480412215.git-series.gregory.clement@free-electrons.com>

Actually only the mvneta_bm support is not 64-bits compatible.
The mvneta code itself can run on 64-bits architecture.

Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 drivers/net/ethernet/marvell/Kconfig | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
index 66fd9dbb2ca7..2ccea9dd9248 100644
--- a/drivers/net/ethernet/marvell/Kconfig
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -44,6 +44,7 @@ config MVMDIO
 config MVNETA_BM_ENABLE
 	tristate "Marvell Armada 38x/XP network interface BM support"
 	depends on MVNETA
+	depends on !64BIT
 	---help---
 	  This driver supports auxiliary block of the network
 	  interface units in the Marvell ARMADA XP and ARMADA 38x SoC
@@ -58,7 +59,6 @@ config MVNETA
 	tristate "Marvell Armada 370/38x/XP network interface support"
 	depends on PLAT_ORION || COMPILE_TEST
 	depends on HAS_DMA
-	depends on !64BIT
 	select MVMDIO
 	select FIXED_PHY
 	---help---
@@ -71,6 +71,7 @@ config MVNETA
 
 config MVNETA_BM
 	tristate
+	depends on !64BIT
 	default y if MVNETA=y && MVNETA_BM_ENABLE!=n
 	default MVNETA_BM_ENABLE
 	select HWBM
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v3 net-next 5/6] net: mvneta: Add network support for Armada 3700 SoC
From: Gregory CLEMENT @ 2016-11-29  9:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.a09aeaf5ba62853d595153626b6121ef52a3e9b4.1480412215.git-series.gregory.clement@free-electrons.com>

From: Marcin Wojtas <mw@semihalf.com>

Armada 3700 is a new ARMv8 SoC from Marvell using same network controller
as older Armada 370/38x/XP. There are however some differences that
needed taking into account when adding support for it:

* open default MBUS window to 4GB of DRAM - Armada 3700 SoC's Mbus
  configuration for network controller has to be done on two levels:
  global and per-port. The first one is inherited from the
  bootloader. The latter can be opened in a default way, leaving
  arbitration to the bus controller.  Hence filled mbus_dram_target_info
  structure is not needed

* make per-CPU operation optional - Recent patches adding RSS and XPS
  support for Armada 38x/XP enabled per-CPU operation of the controller
  by default. Contrary to older SoC's Armada 3700 SoC's network
  controller is not capable of per-CPU processing due to interrupt lines'
  connectivity.  This patch restores non-per-CPU operation, which is now
  optional and depends on neta_armada3700 flag value in mvneta_port
  structure. In order not to complicate the code, separate interrupt
  subroutine is implemented.

For now, on the Armada 3700, RSS is disabled as the current
implementation depend on the per cpu interrupts.

[gregory.clement at free-electrons.com: extract from a larger patch, replace
some ifdef and port to net-next for v4.10]

Signed-off-by: Marcin Wojtas <mw@semihalf.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt |   7 +-
 drivers/net/ethernet/marvell/Kconfig                              |   7 +-
 drivers/net/ethernet/marvell/mvneta.c                             | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
 3 files changed, 214 insertions(+), 87 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
index 73be8970815e..7aa840c8768d 100644
--- a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
+++ b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
@@ -1,7 +1,10 @@
-* Marvell Armada 370 / Armada XP Ethernet Controller (NETA)
+* Marvell Armada 370 / Armada XP / Armada 3700 Ethernet Controller (NETA)
 
 Required properties:
-- compatible: "marvell,armada-370-neta" or "marvell,armada-xp-neta".
+- compatible: could be one of the followings
+	"marvell,armada-370-neta"
+	"marvell,armada-xp-neta"
+	"marvell,armada-3700-neta"
 - reg: address and length of the register set for the device.
 - interrupts: interrupt for the device
 - phy: See ethernet.txt file in the same directory.
diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
index 2ccea9dd9248..3b8f11fe5e13 100644
--- a/drivers/net/ethernet/marvell/Kconfig
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -56,14 +56,15 @@ config MVNETA_BM_ENABLE
 	  buffer management.
 
 config MVNETA
-	tristate "Marvell Armada 370/38x/XP network interface support"
-	depends on PLAT_ORION || COMPILE_TEST
+	tristate "Marvell Armada 370/38x/XP/37xx network interface support"
+	depends on ARCH_MVEBU || COMPILE_TEST
 	depends on HAS_DMA
 	select MVMDIO
 	select FIXED_PHY
 	---help---
 	  This driver supports the network interface units in the
-	  Marvell ARMADA XP, ARMADA 370 and ARMADA 38x SoC family.
+	  Marvell ARMADA XP, ARMADA 370, ARMADA 38x and
+	  ARMADA 37xx SoC family.
 
 	  Note that this driver is distinct from the mv643xx_eth
 	  driver, which should be used for the older Marvell SoCs
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index a8bd0d83028f..99cee88d5052 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -397,6 +397,9 @@ struct mvneta_port {
 	spinlock_t lock;
 	bool is_stopped;
 
+	u32 cause_rx_tx;
+	struct napi_struct napi;
+
 	/* Core clock */
 	struct clk *clk;
 	/* AXI clock */
@@ -422,6 +425,9 @@ struct mvneta_port {
 	u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
 
 	u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
+
+	/* Flags for special SoC configurations */
+	bool neta_armada3700;
 	u16 rx_offset_correction;
 };
 
@@ -965,14 +971,9 @@ static int mvneta_mbus_io_win_set(struct mvneta_port *pp, u32 base, u32 wsize,
 	return 0;
 }
 
-/* Assign and initialize pools for port. In case of fail
- * buffer manager will remain disabled for current port.
- */
-static int mvneta_bm_port_init(struct platform_device *pdev,
-			       struct mvneta_port *pp)
+static  int mvneta_bm_port_mbus_init(struct mvneta_port *pp)
 {
-	struct device_node *dn = pdev->dev.of_node;
-	u32 long_pool_id, short_pool_id, wsize;
+	u32 wsize;
 	u8 target, attr;
 	int err;
 
@@ -991,6 +992,25 @@ static int mvneta_bm_port_init(struct platform_device *pdev,
 		netdev_info(pp->dev, "fail to configure mbus window to BM\n");
 		return err;
 	}
+	return 0;
+}
+
+/* Assign and initialize pools for port. In case of fail
+ * buffer manager will remain disabled for current port.
+ */
+static int mvneta_bm_port_init(struct platform_device *pdev,
+			       struct mvneta_port *pp)
+{
+	struct device_node *dn = pdev->dev.of_node;
+	u32 long_pool_id, short_pool_id;
+
+	if (!pp->neta_armada3700) {
+		int ret;
+
+		ret = mvneta_bm_port_mbus_init(pp);
+		if (ret)
+			return ret;
+	}
 
 	if (of_property_read_u32(dn, "bm,pool-long", &long_pool_id)) {
 		netdev_info(pp->dev, "missing long pool id\n");
@@ -1359,22 +1379,27 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
 	for_each_present_cpu(cpu) {
 		int rxq_map = 0, txq_map = 0;
 		int rxq, txq;
+		if (!pp->neta_armada3700) {
+			for (rxq = 0; rxq < rxq_number; rxq++)
+				if ((rxq % max_cpu) == cpu)
+					rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq);
+
+			for (txq = 0; txq < txq_number; txq++)
+				if ((txq % max_cpu) == cpu)
+					txq_map |= MVNETA_CPU_TXQ_ACCESS(txq);
+
+			/* With only one TX queue we configure a special case
+			 * which will allow to get all the irq on a single
+			 * CPU
+			 */
+			if (txq_number == 1)
+				txq_map = (cpu == pp->rxq_def) ?
+					MVNETA_CPU_TXQ_ACCESS(1) : 0;
 
-		for (rxq = 0; rxq < rxq_number; rxq++)
-			if ((rxq % max_cpu) == cpu)
-				rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq);
-
-		for (txq = 0; txq < txq_number; txq++)
-			if ((txq % max_cpu) == cpu)
-				txq_map |= MVNETA_CPU_TXQ_ACCESS(txq);
-
-		/* With only one TX queue we configure a special case
-		 * which will allow to get all the irq on a single
-		 * CPU
-		 */
-		if (txq_number == 1)
-			txq_map = (cpu == pp->rxq_def) ?
-				MVNETA_CPU_TXQ_ACCESS(1) : 0;
+		} else {
+			txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK;
+			rxq_map = MVNETA_CPU_RXQ_ACCESS_ALL_MASK;
+		}
 
 		mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map);
 	}
@@ -2632,6 +2657,17 @@ static void mvneta_set_rx_mode(struct net_device *dev)
 /* Interrupt handling - the callback for request_irq() */
 static irqreturn_t mvneta_isr(int irq, void *dev_id)
 {
+	struct mvneta_port *pp = (struct mvneta_port *)dev_id;
+
+	mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
+	napi_schedule(&pp->napi);
+
+	return IRQ_HANDLED;
+}
+
+/* Interrupt handling - the callback for request_percpu_irq() */
+static irqreturn_t mvneta_percpu_isr(int irq, void *dev_id)
+{
 	struct mvneta_pcpu_port *port = (struct mvneta_pcpu_port *)dev_id;
 
 	disable_percpu_irq(port->pp->dev->irq);
@@ -2679,7 +2715,7 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
 	struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
 
 	if (!netif_running(pp->dev)) {
-		napi_complete(&port->napi);
+		napi_complete(napi);
 		return rx_done;
 	}
 
@@ -2708,7 +2744,8 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
 	 */
 	rx_queue = fls(((cause_rx_tx >> 8) & 0xff));
 
-	cause_rx_tx |= port->cause_rx_tx;
+	cause_rx_tx |= pp->neta_armada3700 ? pp->cause_rx_tx :
+		port->cause_rx_tx;
 
 	if (rx_queue) {
 		rx_queue = rx_queue - 1;
@@ -2722,11 +2759,27 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
 
 	if (budget > 0) {
 		cause_rx_tx = 0;
-		napi_complete(&port->napi);
-		enable_percpu_irq(pp->dev->irq, 0);
+		napi_complete(napi);
+
+		if (pp->neta_armada3700) {
+			unsigned long flags;
+
+			local_irq_save(flags);
+			mvreg_write(pp, MVNETA_INTR_NEW_MASK,
+				    MVNETA_RX_INTR_MASK(rxq_number) |
+				    MVNETA_TX_INTR_MASK(txq_number) |
+				    MVNETA_MISCINTR_INTR_MASK);
+			local_irq_restore(flags);
+		} else {
+			enable_percpu_irq(pp->dev->irq, 0);
+		}
 	}
 
-	port->cause_rx_tx = cause_rx_tx;
+	if (pp->neta_armada3700)
+		pp->cause_rx_tx = cause_rx_tx;
+	else
+		port->cause_rx_tx = cause_rx_tx;
+
 	return rx_done;
 }
 
@@ -3047,11 +3100,16 @@ static void mvneta_start_dev(struct mvneta_port *pp)
 	/* start the Rx/Tx activity */
 	mvneta_port_enable(pp);
 
-	/* Enable polling on the port */
-	for_each_online_cpu(cpu) {
-		struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
+	if (!pp->neta_armada3700) {
+		/* Enable polling on the port */
+		for_each_online_cpu(cpu) {
+			struct mvneta_pcpu_port *port =
+				per_cpu_ptr(pp->ports, cpu);
 
-		napi_enable(&port->napi);
+			napi_enable(&port->napi);
+		}
+	} else {
+		napi_enable(&pp->napi);
 	}
 
 	/* Unmask interrupts. It has to be done from each CPU */
@@ -3073,10 +3131,15 @@ static void mvneta_stop_dev(struct mvneta_port *pp)
 
 	phy_stop(ndev->phydev);
 
-	for_each_online_cpu(cpu) {
-		struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
+	if (!pp->neta_armada3700) {
+		for_each_online_cpu(cpu) {
+			struct mvneta_pcpu_port *port =
+				per_cpu_ptr(pp->ports, cpu);
 
-		napi_disable(&port->napi);
+			napi_disable(&port->napi);
+		}
+	} else {
+		napi_disable(&pp->napi);
 	}
 
 	netif_carrier_off(pp->dev);
@@ -3486,31 +3549,37 @@ static int mvneta_open(struct net_device *dev)
 		goto err_cleanup_rxqs;
 
 	/* Connect to port interrupt line */
-	ret = request_percpu_irq(pp->dev->irq, mvneta_isr,
-				 MVNETA_DRIVER_NAME, pp->ports);
+	if (pp->neta_armada3700)
+		ret = request_irq(pp->dev->irq, mvneta_isr, 0,
+				  dev->name, pp);
+	else
+		ret = request_percpu_irq(pp->dev->irq, mvneta_percpu_isr,
+					 dev->name, pp->ports);
 	if (ret) {
 		netdev_err(pp->dev, "cannot request irq %d\n", pp->dev->irq);
 		goto err_cleanup_txqs;
 	}
 
-	/* Enable per-CPU interrupt on all the CPU to handle our RX
-	 * queue interrupts
-	 */
-	on_each_cpu(mvneta_percpu_enable, pp, true);
+	if (!pp->neta_armada3700) {
+		/* Enable per-CPU interrupt on all the CPU to handle our RX
+		 * queue interrupts
+		 */
+		on_each_cpu(mvneta_percpu_enable, pp, true);
 
-	pp->is_stopped = false;
-	/* Register a CPU notifier to handle the case where our CPU
-	 * might be taken offline.
-	 */
-	ret = cpuhp_state_add_instance_nocalls(online_hpstate,
-					       &pp->node_online);
-	if (ret)
-		goto err_free_irq;
+		pp->is_stopped = false;
+		/* Register a CPU notifier to handle the case where our CPU
+		 * might be taken offline.
+		 */
+		ret = cpuhp_state_add_instance_nocalls(online_hpstate,
+						       &pp->node_online);
+		if (ret)
+			goto err_free_irq;
 
-	ret = cpuhp_state_add_instance_nocalls(CPUHP_NET_MVNETA_DEAD,
-					       &pp->node_dead);
-	if (ret)
-		goto err_free_online_hp;
+		ret = cpuhp_state_add_instance_nocalls(CPUHP_NET_MVNETA_DEAD,
+						       &pp->node_dead);
+		if (ret)
+			goto err_free_online_hp;
+	}
 
 	/* In default link is down */
 	netif_carrier_off(pp->dev);
@@ -3526,13 +3595,20 @@ static int mvneta_open(struct net_device *dev)
 	return 0;
 
 err_free_dead_hp:
-	cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD,
-					    &pp->node_dead);
+	if (!pp->neta_armada3700)
+		cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD,
+						    &pp->node_dead);
 err_free_online_hp:
-	cpuhp_state_remove_instance_nocalls(online_hpstate, &pp->node_online);
+	if (!pp->neta_armada3700)
+		cpuhp_state_remove_instance_nocalls(online_hpstate,
+						    &pp->node_online);
 err_free_irq:
-	on_each_cpu(mvneta_percpu_disable, pp, true);
-	free_percpu_irq(pp->dev->irq, pp->ports);
+	if (pp->neta_armada3700) {
+		free_irq(pp->dev->irq, pp);
+	} else {
+		on_each_cpu(mvneta_percpu_disable, pp, true);
+		free_percpu_irq(pp->dev->irq, pp->ports);
+	}
 err_cleanup_txqs:
 	mvneta_cleanup_txqs(pp);
 err_cleanup_rxqs:
@@ -3545,23 +3621,30 @@ static int mvneta_stop(struct net_device *dev)
 {
 	struct mvneta_port *pp = netdev_priv(dev);
 
-	/* Inform that we are stopping so we don't want to setup the
-	 * driver for new CPUs in the notifiers. The code of the
-	 * notifier for CPU online is protected by the same spinlock,
-	 * so when we get the lock, the notifer work is done.
-	 */
-	spin_lock(&pp->lock);
-	pp->is_stopped = true;
-	spin_unlock(&pp->lock);
+	if (!pp->neta_armada3700) {
+		/* Inform that we are stopping so we don't want to setup the
+		 * driver for new CPUs in the notifiers. The code of the
+		 * notifier for CPU online is protected by the same spinlock,
+		 * so when we get the lock, the notifer work is done.
+		 */
+		spin_lock(&pp->lock);
+		pp->is_stopped = true;
+		spin_unlock(&pp->lock);
 
-	mvneta_stop_dev(pp);
-	mvneta_mdio_remove(pp);
+		mvneta_stop_dev(pp);
+		mvneta_mdio_remove(pp);
 
 	cpuhp_state_remove_instance_nocalls(online_hpstate, &pp->node_online);
 	cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD,
 					    &pp->node_dead);
-	on_each_cpu(mvneta_percpu_disable, pp, true);
-	free_percpu_irq(dev->irq, pp->ports);
+		on_each_cpu(mvneta_percpu_disable, pp, true);
+		free_percpu_irq(dev->irq, pp->ports);
+	} else {
+		mvneta_stop_dev(pp);
+		mvneta_mdio_remove(pp);
+		free_irq(dev->irq, pp);
+	}
+
 	mvneta_cleanup_rxqs(pp);
 	mvneta_cleanup_txqs(pp);
 
@@ -3840,6 +3923,11 @@ static int mvneta_ethtool_set_rxfh(struct net_device *dev, const u32 *indir,
 				   const u8 *key, const u8 hfunc)
 {
 	struct mvneta_port *pp = netdev_priv(dev);
+
+	/* Current code for Armada 3700 doesn't support RSS features yet */
+	if (pp->neta_armada3700)
+		return -EOPNOTSUPP;
+
 	/* We require at least one supported parameter to be changed
 	 * and no change in any of the unsupported parameters
 	 */
@@ -3860,6 +3948,10 @@ static int mvneta_ethtool_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
 {
 	struct mvneta_port *pp = netdev_priv(dev);
 
+	/* Current code for Armada 3700 doesn't support RSS features yet */
+	if (pp->neta_armada3700)
+		return -EOPNOTSUPP;
+
 	if (hfunc)
 		*hfunc = ETH_RSS_HASH_TOP;
 
@@ -3967,16 +4059,29 @@ static void mvneta_conf_mbus_windows(struct mvneta_port *pp,
 	win_enable = 0x3f;
 	win_protect = 0;
 
-	for (i = 0; i < dram->num_cs; i++) {
-		const struct mbus_dram_window *cs = dram->cs + i;
-		mvreg_write(pp, MVNETA_WIN_BASE(i), (cs->base & 0xffff0000) |
-			    (cs->mbus_attr << 8) | dram->mbus_dram_target_id);
+	if (dram) {
+		for (i = 0; i < dram->num_cs; i++) {
+			const struct mbus_dram_window *cs = dram->cs + i;
+
+			mvreg_write(pp, MVNETA_WIN_BASE(i),
+				    (cs->base & 0xffff0000) |
+				    (cs->mbus_attr << 8) |
+				    dram->mbus_dram_target_id);
 
-		mvreg_write(pp, MVNETA_WIN_SIZE(i),
-			    (cs->size - 1) & 0xffff0000);
+			mvreg_write(pp, MVNETA_WIN_SIZE(i),
+				    (cs->size - 1) & 0xffff0000);
 
-		win_enable &= ~(1 << i);
-		win_protect |= 3 << (2 * i);
+			win_enable &= ~(1 << i);
+			win_protect |= 3 << (2 * i);
+		}
+	} else {
+		/* For Armada3700 open default 4GB Mbus window, leaving
+		 * arbitration of target/attribute to a different layer
+		 * of configuration.
+		 */
+		mvreg_write(pp, MVNETA_WIN_SIZE(0), 0xffff0000);
+		win_enable &= ~BIT(0);
+		win_protect = 3;
 	}
 
 	mvreg_write(pp, MVNETA_BASE_ADDR_ENABLE, win_enable);
@@ -4106,6 +4211,10 @@ static int mvneta_probe(struct platform_device *pdev)
 
 	pp->indir[0] = rxq_def;
 
+	/* Get special SoC configurations */
+	if (of_device_is_compatible(dn, "marvell,armada-3700-neta"))
+		pp->neta_armada3700 = true;
+
 	pp->clk = devm_clk_get(&pdev->dev, "core");
 	if (IS_ERR(pp->clk))
 		pp->clk = devm_clk_get(&pdev->dev, NULL);
@@ -4173,7 +4282,11 @@ static int mvneta_probe(struct platform_device *pdev)
 	pp->tx_csum_limit = tx_csum_limit;
 
 	dram_target_info = mv_mbus_dram_info();
-	if (dram_target_info)
+	/* Armada3700 requires setting default configuration of Mbus
+	 * windows, however without using filled mbus_dram_target_info
+	 * structure.
+	 */
+	if (dram_target_info || pp->neta_armada3700)
 		mvneta_conf_mbus_windows(pp, dram_target_info);
 
 	pp->tx_ring_size = MVNETA_MAX_TXD;
@@ -4206,11 +4319,20 @@ static int mvneta_probe(struct platform_device *pdev)
 		goto err_netdev;
 	}
 
-	for_each_present_cpu(cpu) {
-		struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
+	/* Armada3700 network controller does not support per-cpu
+	 * operation, so only single NAPI should be initialized.
+	 */
+	if (pp->neta_armada3700) {
+		netif_napi_add(dev, &pp->napi, mvneta_poll, NAPI_POLL_WEIGHT);
+	} else {
+		for_each_present_cpu(cpu) {
+			struct mvneta_pcpu_port *port =
+				per_cpu_ptr(pp->ports, cpu);
 
-		netif_napi_add(dev, &port->napi, mvneta_poll, NAPI_POLL_WEIGHT);
-		port->pp = pp;
+			netif_napi_add(dev, &port->napi, mvneta_poll,
+				       NAPI_POLL_WEIGHT);
+			port->pp = pp;
+		}
 	}
 
 	dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
@@ -4295,6 +4417,7 @@ static int mvneta_remove(struct platform_device *pdev)
 static const struct of_device_id mvneta_match[] = {
 	{ .compatible = "marvell,armada-370-neta" },
 	{ .compatible = "marvell,armada-xp-neta" },
+	{ .compatible = "marvell,armada-3700-neta" },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, mvneta_match);
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH v3 net-next 6/6] ARM64: dts: marvell: Add network support for Armada 3700
From: Gregory CLEMENT @ 2016-11-29  9:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.a09aeaf5ba62853d595153626b6121ef52a3e9b4.1480412215.git-series.gregory.clement@free-electrons.com>

Add neta nodes for network support both in device tree for the SoC and
the board.

Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 arch/arm64/boot/dts/marvell/armada-3720-db.dts | 23 +++++++++++++++++++-
 arch/arm64/boot/dts/marvell/armada-37xx.dtsi   | 23 +++++++++++++++++++-
 2 files changed, 46 insertions(+), 0 deletions(-)

diff --git a/arch/arm64/boot/dts/marvell/armada-3720-db.dts b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
index 1372e9a6aaa4..c8b82e4145de 100644
--- a/arch/arm64/boot/dts/marvell/armada-3720-db.dts
+++ b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
@@ -81,3 +81,26 @@
 &pcie0 {
 	status = "okay";
 };
+
+&mdio {
+	status = "okay";
+	phy0: ethernet-phy at 0 {
+		reg = <0>;
+	};
+
+	phy1: ethernet-phy at 1 {
+		reg = <1>;
+	};
+};
+
+&eth0 {
+	phy-mode = "rgmii-id";
+	phy = <&phy0>;
+	status = "okay";
+};
+
+&eth1 {
+	phy-mode = "rgmii-id";
+	phy = <&phy1>;
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
index e9bd58793464..3b8eb45bdc76 100644
--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
@@ -140,6 +140,29 @@
 				};
 			};
 
+			eth0: ethernet at 30000 {
+				   compatible = "marvell,armada-3700-neta";
+				   reg = <0x30000 0x4000>;
+				   interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
+				   clocks = <&sb_periph_clk 8>;
+				   status = "disabled";
+			};
+
+			mdio: mdio at 32004 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "marvell,orion-mdio";
+				reg = <0x32004 0x4>;
+			};
+
+			eth1: ethernet at 40000 {
+				compatible = "marvell,armada-3700-neta";
+				reg = <0x40000 0x4000>;
+				interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&sb_periph_clk 7>;
+				status = "disabled";
+			};
+
 			usb3: usb at 58000 {
 				compatible = "marvell,armada3700-xhci",
 				"generic-xhci";
-- 
git-series 0.8.10

^ permalink raw reply related


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