Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC v3 PATCH 0/2] Introduce Security Version to EFI Stub
From: Ingo Molnar @ 2017-12-07  6:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171207015903.jaos5siysggzz4nc@GaryWorkstation>


* Gary Lin <glin@suse.com> wrote:

> On Wed, Dec 06, 2017 at 07:37:34PM +0100, Ingo Molnar wrote:
> > 
> > * Gary Lin <glin@suse.com> wrote:
> > 
> > > On Tue, Dec 05, 2017 at 04:14:26PM -0500, Josh Boyer wrote:
> > > > On Tue, Dec 5, 2017 at 5:01 AM, Gary Lin <glin@suse.com> wrote:
> > > > > The series of patches introduce Security Version to EFI stub.
> > > > >
> > > > > Security Version is a monotonically increasing number and designed to
> > > > > prevent the user from loading an insecure kernel accidentally. The
> > > > > bootloader maintains a list of security versions corresponding to
> > > > > different distributions. After fixing a critical vulnerability, the
> > > > > distribution kernel maintainer bumps the "version", and the bootloader
> > > > > updates the list automatically. When the user tries to load a kernel
> > > > > with a lower security version, the bootloader shows a warning prompt
> > > > > to notify the user the potential risk.
> > > > 
> > > > If a distribution releases a kernel with a higher security version and
> > > > that it automatically updated on boot, what happens if that kernel
> > > > contains a different bug that causes it to fail to boot or break
> > > > critical functionality?  At that point, the user's machine would be in
> > > > a state where the higher security version is enforced but the only
> > > > kernel that provides that is broken.  Wouldn't that make a bad
> > > > situation even worse by now requiring manual acceptance of the older
> > > > SV kernel boot physically at the machine?
> > > > 
> > > > I feel like I'm missing a detail here or something.
> > > > 
> > > If the new kernel fails to boot, then the user has to choose the kernel
> > > manually anyway, and there will be an option in the warning prompt to
> > > lower SV.
> > 
> > And what if the firmware does not support a lowering of the SV?
> > 
> The SV list is manipulated by the bootloader, and the firmware only
> provides the interface to the storage, i.e. non-volatile flash.

What about systems where the bootloader is part of the system and users only have 
the ability to provide kernel images, but no ability to change the boot loader?

Thanks,

	Ingo

^ permalink raw reply

* [PATCH v3 08/15] drm/sun4i: Add LVDS support
From: Chen-Yu Tsai @ 2017-12-07  6:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <b900e5963180c94ca02bde178b6674622a127787.1512486553.git-series.maxime.ripard@free-electrons.com>

On Tue, Dec 5, 2017 at 11:10 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> The TCON supports the LVDS interface to output to a panel or a bridge.
> Let's add support for it.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/gpu/drm/sun4i/Makefile     |   1 +-
>  drivers/gpu/drm/sun4i/sun4i_lvds.c | 183 +++++++++++++++++++++++-
>  drivers/gpu/drm/sun4i/sun4i_lvds.h |  18 ++-
>  drivers/gpu/drm/sun4i/sun4i_tcon.c | 238 +++++++++++++++++++++++++++++-
>  drivers/gpu/drm/sun4i/sun4i_tcon.h |  29 ++++-
>  5 files changed, 467 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/gpu/drm/sun4i/sun4i_lvds.c
>  create mode 100644 drivers/gpu/drm/sun4i/sun4i_lvds.h
>
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index 82a6ac57fbe3..2b37a6abbb1d 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -15,6 +15,7 @@ sun8i-mixer-y                 += sun8i_mixer.o sun8i_ui_layer.o \
>
>  sun4i-tcon-y                   += sun4i_crtc.o
>  sun4i-tcon-y                   += sun4i_dotclock.o
> +sun4i-tcon-y                   += sun4i_lvds.o
>  sun4i-tcon-y                   += sun4i_tcon.o
>  sun4i-tcon-y                   += sun4i_rgb.o
>
> diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> new file mode 100644
> index 000000000000..635a3f505ecb
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> @@ -0,0 +1,183 @@
> +/*
> + * Copyright (C) 2015 NextThing Co
> + * Copyright (C) 2015-2017 Free Electrons
> + *
> + * Maxime Ripard <maxime.ripard@free-electrons.com>
> + *
> + * 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/clk.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +
> +#include "sun4i_crtc.h"
> +#include "sun4i_tcon.h"
> +#include "sun4i_lvds.h"
> +
> +struct sun4i_lvds {
> +       struct drm_connector    connector;
> +       struct drm_encoder      encoder;
> +
> +       struct sun4i_tcon       *tcon;
> +};
> +
> +static inline struct sun4i_lvds *
> +drm_connector_to_sun4i_lvds(struct drm_connector *connector)
> +{
> +       return container_of(connector, struct sun4i_lvds,
> +                           connector);
> +}
> +
> +static inline struct sun4i_lvds *
> +drm_encoder_to_sun4i_lvds(struct drm_encoder *encoder)
> +{
> +       return container_of(encoder, struct sun4i_lvds,
> +                           encoder);
> +}
> +
> +static int sun4i_lvds_get_modes(struct drm_connector *connector)
> +{
> +       struct sun4i_lvds *lvds =
> +               drm_connector_to_sun4i_lvds(connector);
> +       struct sun4i_tcon *tcon = lvds->tcon;
> +
> +       return drm_panel_get_modes(tcon->panel);
> +}
> +
> +static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
> +       .get_modes      = sun4i_lvds_get_modes,
> +};
> +
> +static void
> +sun4i_lvds_connector_destroy(struct drm_connector *connector)
> +{
> +       struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector);
> +       struct sun4i_tcon *tcon = lvds->tcon;
> +
> +       drm_panel_detach(tcon->panel);
> +       drm_connector_cleanup(connector);
> +}
> +
> +static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
> +       .fill_modes             = drm_helper_probe_single_connector_modes,
> +       .destroy                = sun4i_lvds_connector_destroy,
> +       .reset                  = drm_atomic_helper_connector_reset,
> +       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +       .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
> +{
> +       struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
> +       struct sun4i_tcon *tcon = lvds->tcon;
> +
> +       DRM_DEBUG_DRIVER("Enabling LVDS output\n");
> +
> +       if (!IS_ERR(tcon->panel)) {
> +               drm_panel_prepare(tcon->panel);
> +               drm_panel_enable(tcon->panel);
> +       }
> +}
> +
> +static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
> +{
> +       struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
> +       struct sun4i_tcon *tcon = lvds->tcon;
> +
> +       DRM_DEBUG_DRIVER("Disabling LVDS output\n");
> +
> +       if (!IS_ERR(tcon->panel)) {
> +               drm_panel_disable(tcon->panel);
> +               drm_panel_unprepare(tcon->panel);
> +       }
> +}
> +
> +static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
> +       .disable        = sun4i_lvds_encoder_disable,
> +       .enable         = sun4i_lvds_encoder_enable,
> +};
> +
> +static const struct drm_encoder_funcs sun4i_lvds_enc_funcs = {
> +       .destroy        = drm_encoder_cleanup,
> +};
> +
> +int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
> +{
> +       struct drm_encoder *encoder;
> +       struct drm_bridge *bridge;
> +       struct sun4i_lvds *lvds;
> +       int ret;
> +
> +       lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL);
> +       if (!lvds)
> +               return -ENOMEM;
> +       lvds->tcon = tcon;
> +       encoder = &lvds->encoder;
> +
> +       ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
> +                                         &tcon->panel, &bridge);
> +       if (ret) {
> +               dev_info(drm->dev, "No panel or bridge found... LVDS output disabled\n");
> +               return 0;
> +       }
> +
> +       drm_encoder_helper_add(&lvds->encoder,
> +                              &sun4i_lvds_enc_helper_funcs);
> +       ret = drm_encoder_init(drm,
> +                              &lvds->encoder,
> +                              &sun4i_lvds_enc_funcs,
> +                              DRM_MODE_ENCODER_LVDS,
> +                              NULL);
> +       if (ret) {
> +               dev_err(drm->dev, "Couldn't initialise the lvds encoder\n");
> +               goto err_out;
> +       }
> +
> +       /* The LVDS encoder can only work with the TCON channel 0 */
> +       lvds->encoder.possible_crtcs = BIT(drm_crtc_index(&tcon->crtc->crtc));
> +
> +       if (tcon->panel) {
> +               drm_connector_helper_add(&lvds->connector,
> +                                        &sun4i_lvds_con_helper_funcs);
> +               ret = drm_connector_init(drm, &lvds->connector,
> +                                        &sun4i_lvds_con_funcs,
> +                                        DRM_MODE_CONNECTOR_LVDS);
> +               if (ret) {
> +                       dev_err(drm->dev, "Couldn't initialise the lvds connector\n");
> +                       goto err_cleanup_connector;
> +               }
> +
> +               drm_mode_connector_attach_encoder(&lvds->connector,
> +                                                 &lvds->encoder);
> +
> +               ret = drm_panel_attach(tcon->panel, &lvds->connector);
> +               if (ret) {
> +                       dev_err(drm->dev, "Couldn't attach our panel\n");
> +                       goto err_cleanup_connector;
> +               }
> +       }
> +
> +       if (bridge) {
> +               ret = drm_bridge_attach(encoder, bridge, NULL);
> +               if (ret) {
> +                       dev_err(drm->dev, "Couldn't attach our bridge\n");
> +                       goto err_cleanup_connector;
> +               }
> +       }
> +
> +       return 0;
> +
> +err_cleanup_connector:
> +       drm_encoder_cleanup(&lvds->encoder);
> +err_out:
> +       return ret;
> +}
> +EXPORT_SYMBOL(sun4i_lvds_init);
> diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.h b/drivers/gpu/drm/sun4i/sun4i_lvds.h
> new file mode 100644
> index 000000000000..1b8fad4b82c3
> --- /dev/null
> +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.h
> @@ -0,0 +1,18 @@
> +/*
> + * Copyright (C) 2015 NextThing Co
> + * Copyright (C) 2015-2017 Free Electrons
> + *
> + * Maxime Ripard <maxime.ripard@free-electrons.com>
> + *
> + * 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.
> + */
> +
> +#ifndef _SUN4I_LVDS_H_
> +#define _SUN4I_LVDS_H_
> +
> +int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon);
> +
> +#endif /* _SUN4I_LVDS_H_ */
> diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
> index 46e28ca1f676..92f4738101e6 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
> @@ -31,10 +31,52 @@
>  #include "sun4i_crtc.h"
>  #include "sun4i_dotclock.h"
>  #include "sun4i_drv.h"
> +#include "sun4i_lvds.h"
>  #include "sun4i_rgb.h"
>  #include "sun4i_tcon.h"
>  #include "sunxi_engine.h"
>
> +static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder)
> +{
> +       struct drm_connector *connector;
> +       struct drm_connector_list_iter iter;
> +
> +       drm_connector_list_iter_begin(encoder->dev, &iter);
> +       drm_for_each_connector_iter(connector, &iter)
> +               if (connector->encoder == encoder) {
> +                       drm_connector_list_iter_end(&iter);
> +                       return connector;
> +               }
> +       drm_connector_list_iter_end(&iter);
> +
> +       return NULL;
> +}
> +
> +static int sun4i_tcon_get_pixel_depth(const struct drm_encoder *encoder)
> +{
> +       struct drm_connector *connector;
> +       struct drm_display_info *info;
> +
> +       connector = sun4i_tcon_get_connector(encoder);
> +       if (!connector)
> +               return -EINVAL;
> +
> +       info = &connector->display_info;
> +       if (info->num_bus_formats != 1)
> +               return -EINVAL;
> +
> +       switch (info->bus_formats[0]) {
> +       case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
> +               return 18;
> +
> +       case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
> +       case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
> +               return 24;
> +       }
> +
> +       return -EINVAL;
> +}
> +
>  static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel,
>                                           bool enabled)
>  {
> @@ -65,13 +107,58 @@ static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel,
>                 clk_disable_unprepare(clk);
>  }
>
> +static void sun4i_tcon_lvds_set_status(struct sun4i_tcon *tcon,
> +                                      const struct drm_encoder *encoder,
> +                                      bool enabled)
> +{
> +       if (enabled) {
> +               u8 val;
> +
> +               regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
> +                                  SUN4I_TCON0_LVDS_IF_EN,
> +                                  SUN4I_TCON0_LVDS_IF_EN);
> +
> +               regmap_write(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
> +                            SUN4I_TCON0_LVDS_ANA0_C(2) |
> +                            SUN4I_TCON0_LVDS_ANA0_V(3) |
> +                            SUN4I_TCON0_LVDS_ANA0_PD(2) |
> +                            SUN4I_TCON0_LVDS_ANA0_EN_LDO);
> +               udelay(2);
> +
> +               regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
> +                                  SUN4I_TCON0_LVDS_ANA0_EN_MB,
> +                                  SUN4I_TCON0_LVDS_ANA0_EN_MB);
> +               udelay(2);
> +
> +               regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
> +                                  SUN4I_TCON0_LVDS_ANA0_EN_DRVC,
> +                                  SUN4I_TCON0_LVDS_ANA0_EN_DRVC);
> +
> +               if (sun4i_tcon_get_pixel_depth(encoder) == 18)
> +                       val = 7;
> +               else
> +                       val = 0xf;
> +
> +               regmap_write_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
> +                                 SUN4I_TCON0_LVDS_ANA0_EN_DRVD(0xf),
> +                                 SUN4I_TCON0_LVDS_ANA0_EN_DRVD(val));

I suggest changing the prefix of the macros of the analog bits to
SUN6I_TCON0_*. The register definitions and sequence do not apply
to the A10/A20. Furthermore you should add a comment saying this
doesn't apply to the A10/A20. In the future we might want to move
this part into a separate function, referenced by a function pointer
from the quirks structure.

> +       } else {
> +               regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
> +                                  SUN4I_TCON0_LVDS_IF_EN, 0);
> +       }
> +}
> +
>  void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
>                            const struct drm_encoder *encoder,
>                            bool enabled)
>  {
> +       bool is_lvds = false;
>         int channel;
>
>         switch (encoder->encoder_type) {
> +       case DRM_MODE_ENCODER_LVDS:
> +               is_lvds = true;
> +               /* Fallthrough */
>         case DRM_MODE_ENCODER_NONE:
>                 channel = 0;
>                 break;
> @@ -84,10 +171,16 @@ void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
>                 return;
>         }
>
> +       if (is_lvds && !enabled)
> +               sun4i_tcon_lvds_set_status(tcon, encoder, false);
> +
>         regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
>                            SUN4I_TCON_GCTL_TCON_ENABLE,
>                            enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0);
>
> +       if (is_lvds && enabled)
> +               sun4i_tcon_lvds_set_status(tcon, encoder, true);
> +
>         sun4i_tcon_channel_set_status(tcon, channel, enabled);
>  }
>
> @@ -170,6 +263,78 @@ static void sun4i_tcon0_mode_set_common(struct sun4i_tcon *tcon,
>                      SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
>  }
>
> +static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
> +                                     const struct drm_encoder *encoder,
> +                                     const struct drm_display_mode *mode)
> +{
> +       unsigned int bp;
> +       u8 clk_delay;
> +       u32 reg, val = 0;
> +
> +       tcon->dclk_min_div = 7;
> +       tcon->dclk_max_div = 7;
> +       sun4i_tcon0_mode_set_common(tcon, mode);
> +
> +       /* Adjust clock delay */
> +       clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
> +       regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
> +                          SUN4I_TCON0_CTL_CLK_DELAY_MASK,
> +                          SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));
> +
> +       /*
> +        * This is called a backporch in the register documentation,
> +        * but it really is the back porch + hsync
> +        */
> +       bp = mode->crtc_htotal - mode->crtc_hsync_start;
> +       DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
> +                        mode->crtc_htotal, bp);
> +
> +       /* Set horizontal display timings */
> +       regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG,
> +                    SUN4I_TCON0_BASIC1_H_TOTAL(mode->htotal) |
> +                    SUN4I_TCON0_BASIC1_H_BACKPORCH(bp));
> +
> +       /*
> +        * This is called a backporch in the register documentation,
> +        * but it really is the back porch + hsync
> +        */
> +       bp = mode->crtc_vtotal - mode->crtc_vsync_start;
> +       DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
> +                        mode->crtc_vtotal, bp);
> +
> +       /* Set vertical display timings */
> +       regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG,
> +                    SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |
> +                    SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));
> +

Can we move the above to a common function?

> +       reg = SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0 |
> +               SUN4I_TCON0_LVDS_IF_DATA_POL_NORMAL |
> +               SUN4I_TCON0_LVDS_IF_CLK_POL_NORMAL;
> +       if (sun4i_tcon_get_pixel_depth(encoder) == 24)
> +               reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_24BITS;
> +       else
> +               reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_18BITS;
> +
> +       regmap_write(tcon->regs, SUN4I_TCON0_LVDS_IF_REG, reg);
> +
> +       /* Setup the polarity of the various signals */
> +       if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
> +               val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE;
> +
> +       if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
> +               val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE;
> +
> +       regmap_write(tcon->regs, SUN4I_TCON0_IO_POL_REG, val);
> +
> +       /* Map output pins to channel 0 */
> +       regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
> +                          SUN4I_TCON_GCTL_IOMAP_MASK,
> +                          SUN4I_TCON_GCTL_IOMAP_TCON0);
> +
> +       /* Enable the output on the pins */
> +       regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0xe0000000);

Is this still needed? You are no longer using the TCON LCD pins
with LVDS.

> +}
> +
>  static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon,
>                                      const struct drm_display_mode *mode)
>  {
> @@ -336,6 +501,9 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,
>                          const struct drm_display_mode *mode)
>  {
>         switch (encoder->encoder_type) {
> +       case DRM_MODE_ENCODER_LVDS:
> +               sun4i_tcon0_mode_set_lvds(tcon, encoder, mode);
> +               break;
>         case DRM_MODE_ENCODER_NONE:
>                 sun4i_tcon0_mode_set_rgb(tcon, mode);
>                 sun4i_tcon_set_mux(tcon, 0, encoder);
> @@ -667,7 +835,9 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
>         struct drm_device *drm = data;
>         struct sun4i_drv *drv = drm->dev_private;
>         struct sunxi_engine *engine;
> +       struct device_node *remote;
>         struct sun4i_tcon *tcon;
> +       bool has_lvds_rst, has_lvds_pll, can_lvds;
>         int ret;
>
>         engine = sun4i_tcon_find_engine(drv, dev->of_node);
> @@ -698,6 +868,54 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
>                 return ret;
>         }
>
> +       /*
> +        * This can only be made optional since we've had DT nodes
> +        * without the LVDS reset properties.
> +        *
> +        * If the property is missing, just disable LVDS, and print a
> +        * warning.
> +        */
> +       tcon->lvds_rst = devm_reset_control_get_optional(dev, "lvds");
> +       if (IS_ERR(tcon->lvds_rst)) {
> +               dev_err(dev, "Couldn't get our reset line\n");
> +               return PTR_ERR(tcon->lvds_rst);
> +       } else if (tcon->lvds_rst) {
> +               has_lvds_rst = true;
> +               reset_control_reset(tcon->lvds_rst);
> +       } else {
> +               has_lvds_rst = false;
> +       }
> +
> +       /*
> +        * This can only be made optional since we've had DT nodes
> +        * without the LVDS reset properties.
> +        *
> +        * If the property is missing, just disable LVDS, and print a
> +        * warning.
> +        */
> +       if (tcon->quirks->has_lvds_pll) {
> +               tcon->lvds_pll = devm_clk_get(dev, "pll-lvds");
> +               if (IS_ERR(tcon->lvds_pll)) {
> +                       if (PTR_ERR(tcon->lvds_pll) == -ENOENT) {
> +                               has_lvds_pll = false;
> +                       } else {
> +                               dev_err(dev, "Couldn't get the LVDS PLL\n");
> +                               return PTR_ERR(tcon->lvds_rst);
> +                       }
> +               } else {
> +                       has_lvds_pll = true;
> +               }
> +       }
> +
> +       if (!has_lvds_rst || (tcon->quirks->has_lvds_pll && !has_lvds_pll)) {
> +               dev_warn(dev,
> +                        "Missing LVDS properties, Please upgrade your DT\n");
> +               dev_warn(dev, "LVDS output disabled\n");
> +               can_lvds = false;
> +       } else {
> +               can_lvds = true;
> +       }
> +
>         ret = sun4i_tcon_init_clocks(dev, tcon);
>         if (ret) {
>                 dev_err(dev, "Couldn't init our TCON clocks\n");
> @@ -729,7 +947,21 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
>                 goto err_free_dotclock;
>         }
>
> -       ret = sun4i_rgb_init(drm, tcon);
> +       /*
> +        * If we have an LVDS panel connected to the TCON, we should
> +        * just probe the LVDS connector. Otherwise, just probe RGB as
> +        * we used to.
> +        */
> +       remote = of_graph_get_remote_node(dev->of_node, 1, 0);
> +       if (of_device_is_compatible(remote, "panel-lvds"))
> +               if (can_lvds)
> +                       ret = sun4i_lvds_init(drm, tcon);
> +               else
> +                       ret = -EINVAL;
> +       else
> +               ret = sun4i_rgb_init(drm, tcon);
> +       of_node_put(remote);
> +
>         if (ret < 0)
>                 goto err_free_dotclock;
>
> @@ -879,12 +1111,14 @@ static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
>
>  static const struct sun4i_tcon_quirks sun6i_a31_quirks = {
>         .has_channel_1          = true,
> +       .has_lvds_pll           = true,
>         .needs_de_be_mux        = true,
>         .set_mux                = sun6i_tcon_set_mux,
>  };
>
>  static const struct sun4i_tcon_quirks sun6i_a31s_quirks = {
>         .has_channel_1          = true,
> +       .has_lvds_pll           = true,

The A31s does not have MIPI.

>         .needs_de_be_mux        = true,
>  };
>
> @@ -895,7 +1129,7 @@ static const struct sun4i_tcon_quirks sun7i_a20_quirks = {
>  };
>
>  static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
> -       /* nothing is supported */
> +       .has_lvds_pll           = true,
>  };
>
>  static const struct sun4i_tcon_quirks sun8i_v3s_quirks = {
> diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h
> index bd3ad7684870..6e801a6325a1 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_tcon.h
> +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h
> @@ -70,7 +70,21 @@
>  #define SUN4I_TCON0_TTL2_REG                   0x78
>  #define SUN4I_TCON0_TTL3_REG                   0x7c
>  #define SUN4I_TCON0_TTL4_REG                   0x80
> +
>  #define SUN4I_TCON0_LVDS_IF_REG                        0x84
> +#define SUN4I_TCON0_LVDS_IF_EN                         BIT(31)
> +#define SUN4I_TCON0_LVDS_IF_BITWIDTH_MASK              BIT(26)
> +#define SUN4I_TCON0_LVDS_IF_BITWIDTH_18BITS            (1 << 26)
> +#define SUN4I_TCON0_LVDS_IF_BITWIDTH_24BITS            (0 << 26)
> +#define SUN4I_TCON0_LVDS_IF_CLK_SEL_MASK               BIT(20)
> +#define SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0              (1 << 20)
> +#define SUN4I_TCON0_LVDS_IF_CLK_POL_MASK               BIT(4)
> +#define SUN4I_TCON0_LVDS_IF_CLK_POL_NORMAL             (1 << 4)
> +#define SUN4I_TCON0_LVDS_IF_CLK_POL_INV                        (0 << 4)
> +#define SUN4I_TCON0_LVDS_IF_DATA_POL_MASK              GENMASK(3, 0)
> +#define SUN4I_TCON0_LVDS_IF_DATA_POL_NORMAL            (0xf)
> +#define SUN4I_TCON0_LVDS_IF_DATA_POL_INV               (0)
> +
>  #define SUN4I_TCON0_IO_POL_REG                 0x88
>  #define SUN4I_TCON0_IO_POL_DCLK_PHASE(phase)           ((phase & 3) << 28)
>  #define SUN4I_TCON0_IO_POL_HSYNC_POSITIVE              BIT(25)
> @@ -131,6 +145,16 @@
>  #define SUN4I_TCON_CEU_RANGE_G_REG             0x144
>  #define SUN4I_TCON_CEU_RANGE_B_REG             0x148
>  #define SUN4I_TCON_MUX_CTRL_REG                        0x200
> +
> +#define SUN4I_TCON0_LVDS_ANA0_REG              0x220
> +#define SUN4I_TCON0_LVDS_ANA0_EN_MB                    BIT(31)
> +#define SUN4I_TCON0_LVDS_ANA0_EN_LDO                   BIT(30)
> +#define SUN4I_TCON0_LVDS_ANA0_EN_DRVC                  BIT(24)
> +#define SUN4I_TCON0_LVDS_ANA0_EN_DRVD(x)               (((x) & 0xf) << 20)
> +#define SUN4I_TCON0_LVDS_ANA0_C(x)                     (((x) & 3) << 17)
> +#define SUN4I_TCON0_LVDS_ANA0_V(x)                     (((x) & 3) << 8)
> +#define SUN4I_TCON0_LVDS_ANA0_PD(x)                    (((x) & 3) << 4)

See above about the analog bits.

ChenYu

> +
>  #define SUN4I_TCON1_FILL_CTL_REG               0x300
>  #define SUN4I_TCON1_FILL_BEG0_REG              0x304
>  #define SUN4I_TCON1_FILL_END0_REG              0x308
> @@ -149,6 +173,7 @@ struct sun4i_tcon;
>
>  struct sun4i_tcon_quirks {
>         bool    has_channel_1;  /* a33 does not have channel 1 */
> +       bool    has_lvds_pll;   /* Can we mux the LVDS clock to a PLL? */
>         bool    needs_de_be_mux; /* sun6i needs mux to select backend */
>
>         /* callback to handle tcon muxing options */
> @@ -167,6 +192,9 @@ struct sun4i_tcon {
>         struct clk                      *sclk0;
>         struct clk                      *sclk1;
>
> +       /* Possible mux for the LVDS clock */
> +       struct clk                      *lvds_pll;
> +
>         /* Pixel clock */
>         struct clk                      *dclk;
>         u8                              dclk_max_div;
> @@ -174,6 +202,7 @@ struct sun4i_tcon {
>
>         /* Reset control */
>         struct reset_control            *lcd_rst;
> +       struct reset_control            *lvds_rst;
>
>         struct drm_panel                *panel;
>
> --
> git-series 0.9.1

^ permalink raw reply

* [PATCH v2 2/3] ARM: davinci: Add dma_mask to dm365's eDMA device
From: Sekhar Nori @ 2017-12-07  6:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171205123458.97837-3-amery@hanoverdisplays.com>

On Tuesday 05 December 2017 06:04 PM, Alejandro Mery wrote:
> as previously done by cef5b0da4019358cb03c9b0a964d4d63cd7deaf6 for other davinci SoCs
> 
> Signed-off-by: Alejandro Mery <amery@hanoverdisplays.com>

Most of the comments on 1/2 apply here. Here is the updated description:

--8<--
ARM: davinci: Add dma_mask to dm365's eDMA device

Add dma_mask to dm365's EDMA device.

Without a valid dma_mask, EDMA on DM365 refuses to
probe.

Fixes: cef5b0da4019 ("ARM: davinci: Add dma_mask to eDMA devices")
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Signed-off-by: Alejandro Mery <amery@hanoverdisplays.com>
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
--8<--

Thanks,
Sekhar

> ---
>  arch/arm/mach-davinci/dm365.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c
> index 9bd17bc77b5c..103316f01a22 100644
> --- a/arch/arm/mach-davinci/dm365.c
> +++ b/arch/arm/mach-davinci/dm365.c
> @@ -928,6 +928,7 @@ static struct resource edma_resources[] = {
>  static const struct platform_device_info dm365_edma_device __initconst = {
>  	.name		= "edma",
>  	.id		= 0,
> +	.dma_mask	= DMA_BIT_MASK(32),
>  	.res		= edma_resources,
>  	.num_res	= ARRAY_SIZE(edma_resources),
>  	.data		= &dm365_edma_pdata,
> -- 
> 2.15.0
> 

^ permalink raw reply

* [PATCH v2 1/3] ARM: davinci: Use platform_device_register_full() to create pdev for dm365's eDMA
From: Sekhar Nori @ 2017-12-07  5:57 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171205123458.97837-2-amery@hanoverdisplays.com>

Hi Alejandro,

On Tuesday 05 December 2017 06:04 PM, Alejandro Mery wrote:
> as it was done by 7ab388e85faa97a35d520720269e7c8e00ad54a0 for other davinci SoCs
> 
> changes since v1:
> * fix typo
> 
> Signed-off-by: Alejandro Mery <amery@hanoverdisplays.com>

The patch itself looks good (and apologies for not catching the missing 
DM365 conversion). But:

The way you refer to the commit in patch description will lead to 
checkpatch error. And, we need more explanation in the patch description 
than just referencing the commit which is being fixed. When fixing a 
previous commit, its best to use the Fixes: tag (see below). Thats an 
indication for stable tree maintainers to backport this patch all the 
way till the time the offending commit was introduced into the kernel. 
This way you don't have to do the backporting yourself.

Please keep the changelog below the tearline "---" so it will not get 
included in commit description. And, when submitting another version, 
please carry forward the Reviewed-by: and and Acked-by: tags you 
received for previous version (unless the patch has changed so 
significantly that it does not make sense to do so anymore).

For this time, I fixed all of these up locally. Here is the final commit 
text.

--8<--
ARM: davinci: Use platform_device_register_full() to create pdev for dm365's eDMA

Convert the DM365 EDMA platform device creation to use
struct platform_device_info XXXXXX __initconst and
platform_device_register_full()

This will allow us to specify the dma_mask for the device
in an upcoming patch. Without this, EDMA on DM365 refuses
to probe.

Fixes: 7ab388e85faa ("ARM: davinci: Use platform_device_register_full() to create pdev for eDMA")
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Signed-off-by: Alejandro Mery <amery@hanoverdisplays.com>
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
--8<--

Thanks,
Sekhar


> ---
>  arch/arm/mach-davinci/dm365.c | 20 +++++++++++++-------
>  1 file changed, 13 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c
> index 8be04ec95adf..9bd17bc77b5c 100644
> --- a/arch/arm/mach-davinci/dm365.c
> +++ b/arch/arm/mach-davinci/dm365.c
> @@ -925,12 +925,13 @@ static struct resource edma_resources[] = {
>  	/* not using TC*_ERR */
>  };
>  
> -static struct platform_device dm365_edma_device = {
> -	.name			= "edma",
> -	.id			= 0,
> -	.dev.platform_data	= &dm365_edma_pdata,
> -	.num_resources		= ARRAY_SIZE(edma_resources),
> -	.resource		= edma_resources,
> +static const struct platform_device_info dm365_edma_device __initconst = {
> +	.name		= "edma",
> +	.id		= 0,
> +	.res		= edma_resources,
> +	.num_res	= ARRAY_SIZE(edma_resources),
> +	.data		= &dm365_edma_pdata,
> +	.size_data	= sizeof(dm365_edma_pdata),
>  };
>  
>  static struct resource dm365_asp_resources[] = {
> @@ -1428,13 +1429,18 @@ int __init dm365_init_video(struct vpfe_config *vpfe_cfg,
>  
>  static int __init dm365_init_devices(void)
>  {
> +	struct platform_device *edma_pdev;
>  	int ret = 0;
>  
>  	if (!cpu_is_davinci_dm365())
>  		return 0;
>  
>  	davinci_cfg_reg(DM365_INT_EDMA_CC);
> -	platform_device_register(&dm365_edma_device);
> +	edma_pdev = platform_device_register_full(&dm365_edma_device);
> +	if (IS_ERR(edma_pdev)) {
> +		pr_warn("%s: Failed to register eDMA\n", __func__);
> +		return PTR_ERR(edma_pdev);
> +	}
>  
>  	platform_device_register(&dm365_mdio_device);
>  	platform_device_register(&dm365_emac_device);
> -- 
> 2.15.0
> 

^ permalink raw reply

* [PATCH RESEND] arm64: fault: avoid send SIGBUS two times
From: gengdongjiu @ 2017-12-07  5:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171206161540.GB25408@arm.com>

On 2017/12/7 0:15, Will Deacon wrote:
>> --- a/arch/arm64/mm/fault.c
>> +++ b/arch/arm64/mm/fault.c
>> @@ -570,7 +570,6 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
>>  {
>>  	struct siginfo info;
>>  	const struct fault_info *inf;
>> -	int ret = 0;
>>  
>>  	inf = esr_to_fault_info(esr);
>>  	pr_err("Synchronous External Abort: %s (0x%08x) at 0x%016lx\n",
>> @@ -585,7 +584,7 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
>>  		if (interrupts_enabled(regs))
>>  			nmi_enter();
>>  
>> -		ret = ghes_notify_sea();
>> +		ghes_notify_sea();
>>  
>>  		if (interrupts_enabled(regs))
>>  			nmi_exit();
>> @@ -600,7 +599,7 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
>>  		info.si_addr  = (void __user *)addr;
>>  	arm64_notify_die("", regs, &info, esr);
>>  
>> -	return ret;
>> +	return 0;
> Hmm, so this code is a bit of mess.
> 
> Wouldn't it be better to have the signal dispatching code in do_mem_abort
> check ESR.ESR_ELx_FnV, so then do_sea wouldn't have to, and we could just
> return an error instead?

Thanks the mail and comments!

Regardless ghes_notify_sea()'s return value, it always needs to deliver signal,
because ghes_notify_sea()'s return value does not reflect whether the memory error
handler(memory_failure()) handle the error successfully or failed. If let do_mem_abort()
delivers the signal, we should always let do_sea() return error, then  the do_mem_abort() can
always deliver signal. Then we will see the strange log as shown below when happen Synchronous External Abort.

[  676.700652] Synchronous External Abort: synchronous external abort (0x96000410) at 0x0000000033ff7008
[  676.723301] Unhandled fault: synchronous external abort (0x96000410) at 0x0000000033ff7008

so I think it is better send the signal in the do_sea(), not send it in the do_mem_abort().
do_mem_abort() only send the signal when the exception does not defined in fault_info[]. Another benefit
is that do_sea() can send different signal according to the Synchronous External Abort type, such as SIGBUS or SIGKILL.
the do_mem_abort() can only send one kind signal.

^ permalink raw reply

* [PATCH v6 3/6] kernel/reboot.c: export pm_power_off_prepare
From: Oleksij Rempel @ 2017-12-07  5:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171206231130.GA6235@infradead.org>



On 07.12.2017 00:11, Christoph Hellwig wrote:
>>  void (*pm_power_off_prepare)(void);
>> +EXPORT_SYMBOL(pm_power_off_prepare);
> 
> EXPORT_SYMBOL_GPL for something this deeply internal, please.

Ok,
probably all other symbols should be converted in this file in to
EXPORT_SYMBOL_GPL as well?

grep EXPORT_SYMBOL kernel/reboot.c
EXPORT_SYMBOL(cad_pid);
EXPORT_SYMBOL(pm_power_off_prepare);
EXPORT_SYMBOL_GPL(emergency_restart);
EXPORT_SYMBOL(register_reboot_notifier);
EXPORT_SYMBOL(unregister_reboot_notifier);
EXPORT_SYMBOL(devm_register_reboot_notifier);
EXPORT_SYMBOL(register_restart_handler);
EXPORT_SYMBOL(unregister_restart_handler);
EXPORT_SYMBOL_GPL(kernel_restart);
EXPORT_SYMBOL_GPL(kernel_halt);
EXPORT_SYMBOL_GPL(kernel_power_off);
EXPORT_SYMBOL_GPL(orderly_poweroff);
EXPORT_SYMBOL_GPL(orderly_reboot);

^ permalink raw reply

* [RESEND PATCH v2] dmaengine: zynqmp_dma: Add runtime pm support
From: Kedareswara rao Appana @ 2017-12-07  5:29 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds runtime pm support in the driver.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Resending the patch as there are some issues with my git send-email,
patch is not showing in the dmeengine mailing list.
Changes for v2:
--> Fixed error path in the probe.

 drivers/dma/xilinx/zynqmp_dma.c | 160 ++++++++++++++++++++++++++++++++--------
 1 file changed, 128 insertions(+), 32 deletions(-)

diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
index 1ee1241..4fa14bf 100644
--- a/drivers/dma/xilinx/zynqmp_dma.c
+++ b/drivers/dma/xilinx/zynqmp_dma.c
@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/clk.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/pm_runtime.h>
 
 #include "../dmaengine.h"
 
@@ -138,6 +139,8 @@
 #define ZYNQMP_DMA_BUS_WIDTH_64		64
 #define ZYNQMP_DMA_BUS_WIDTH_128	128
 
+#define ZDMA_PM_TIMEOUT			100
+
 #define ZYNQMP_DMA_DESC_SIZE(chan)	(chan->desc_size)
 
 #define to_chan(chan)		container_of(chan, struct zynqmp_dma_chan, \
@@ -211,8 +214,6 @@ struct zynqmp_dma_desc_sw {
  * @bus_width: Bus width
  * @src_burst_len: Source burst length
  * @dst_burst_len: Dest burst length
- * @clk_main: Pointer to main clock
- * @clk_apb: Pointer to apb clock
  */
 struct zynqmp_dma_chan {
 	struct zynqmp_dma_device *zdev;
@@ -237,8 +238,6 @@ struct zynqmp_dma_chan {
 	u32 bus_width;
 	u32 src_burst_len;
 	u32 dst_burst_len;
-	struct clk *clk_main;
-	struct clk *clk_apb;
 };
 
 /**
@@ -246,11 +245,15 @@ struct zynqmp_dma_chan {
  * @dev: Device Structure
  * @common: DMA device structure
  * @chan: Driver specific DMA channel
+ * @clk_main: Pointer to main clock
+ * @clk_apb: Pointer to apb clock
  */
 struct zynqmp_dma_device {
 	struct device *dev;
 	struct dma_device common;
 	struct zynqmp_dma_chan *chan;
+	struct clk *clk_main;
+	struct clk *clk_apb;
 };
 
 static inline void zynqmp_dma_writeq(struct zynqmp_dma_chan *chan, u32 reg,
@@ -461,7 +464,11 @@ static int zynqmp_dma_alloc_chan_resources(struct dma_chan *dchan)
 {
 	struct zynqmp_dma_chan *chan = to_chan(dchan);
 	struct zynqmp_dma_desc_sw *desc;
-	int i;
+	int i, ret;
+
+	ret = pm_runtime_get_sync(chan->dev);
+	if (ret < 0)
+		return ret;
 
 	chan->sw_desc_pool = kzalloc(sizeof(*desc) * ZYNQMP_DMA_NUM_DESCS,
 				     GFP_KERNEL);
@@ -664,6 +671,8 @@ static void zynqmp_dma_free_chan_resources(struct dma_chan *dchan)
 		(2 * ZYNQMP_DMA_DESC_SIZE(chan) * ZYNQMP_DMA_NUM_DESCS),
 		chan->desc_pool_v, chan->desc_pool_p);
 	kfree(chan->sw_desc_pool);
+	pm_runtime_mark_last_busy(chan->dev);
+	pm_runtime_put_autosuspend(chan->dev);
 }
 
 /**
@@ -841,8 +850,6 @@ static void zynqmp_dma_chan_remove(struct zynqmp_dma_chan *chan)
 	devm_free_irq(chan->zdev->dev, chan->irq, chan);
 	tasklet_kill(&chan->tasklet);
 	list_del(&chan->common.device_node);
-	clk_disable_unprepare(chan->clk_apb);
-	clk_disable_unprepare(chan->clk_main);
 }
 
 /**
@@ -907,30 +914,6 @@ static int zynqmp_dma_chan_probe(struct zynqmp_dma_device *zdev,
 			       "zynqmp-dma", chan);
 	if (err)
 		return err;
-	chan->clk_main = devm_clk_get(&pdev->dev, "clk_main");
-	if (IS_ERR(chan->clk_main)) {
-		dev_err(&pdev->dev, "main clock not found.\n");
-		return PTR_ERR(chan->clk_main);
-	}
-
-	chan->clk_apb = devm_clk_get(&pdev->dev, "clk_apb");
-	if (IS_ERR(chan->clk_apb)) {
-		dev_err(&pdev->dev, "apb clock not found.\n");
-		return PTR_ERR(chan->clk_apb);
-	}
-
-	err = clk_prepare_enable(chan->clk_main);
-	if (err) {
-		dev_err(&pdev->dev, "Unable to enable main clock.\n");
-		return err;
-	}
-
-	err = clk_prepare_enable(chan->clk_apb);
-	if (err) {
-		clk_disable_unprepare(chan->clk_main);
-		dev_err(&pdev->dev, "Unable to enable apb clock.\n");
-		return err;
-	}
 
 	chan->desc_size = sizeof(struct zynqmp_dma_desc_ll);
 	chan->idle = true;
@@ -953,6 +936,87 @@ static struct dma_chan *of_zynqmp_dma_xlate(struct of_phandle_args *dma_spec,
 }
 
 /**
+ * zynqmp_dma_suspend - Suspend method for the driver
+ * @dev:	Address of the device structure
+ *
+ * Put the driver into low power mode.
+ * Return: 0 on success and failure value on error
+ */
+static int __maybe_unused zynqmp_dma_suspend(struct device *dev)
+{
+	if (!device_may_wakeup(dev))
+		return pm_runtime_force_suspend(dev);
+
+	return 0;
+}
+
+/**
+ * zynqmp_dma_resume - Resume from suspend
+ * @dev:	Address of the device structure
+ *
+ * Resume operation after suspend.
+ * Return: 0 on success and failure value on error
+ */
+static int __maybe_unused zynqmp_dma_resume(struct device *dev)
+{
+	if (!device_may_wakeup(dev))
+		return pm_runtime_force_resume(dev);
+
+	return 0;
+}
+
+/**
+ * zynqmp_dma_runtime_suspend - Runtime suspend method for the driver
+ * @dev:	Address of the device structure
+ *
+ * Put the driver into low power mode.
+ * Return: 0 always
+ */
+static int __maybe_unused zynqmp_dma_runtime_suspend(struct device *dev)
+{
+	struct zynqmp_dma_device *zdev = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(zdev->clk_main);
+	clk_disable_unprepare(zdev->clk_apb);
+
+	return 0;
+}
+
+/**
+ * zynqmp_dma_runtime_resume - Runtime suspend method for the driver
+ * @dev:	Address of the device structure
+ *
+ * Put the driver into low power mode.
+ * Return: 0 always
+ */
+static int __maybe_unused zynqmp_dma_runtime_resume(struct device *dev)
+{
+	struct zynqmp_dma_device *zdev = dev_get_drvdata(dev);
+	int err;
+
+	err = clk_prepare_enable(zdev->clk_main);
+	if (err) {
+		dev_err(dev, "Unable to enable main clock.\n");
+		return err;
+	}
+
+	err = clk_prepare_enable(zdev->clk_apb);
+	if (err) {
+		dev_err(dev, "Unable to enable apb clock.\n");
+		clk_disable_unprepare(zdev->clk_main);
+		return err;
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops zynqmp_dma_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(zynqmp_dma_suspend, zynqmp_dma_resume)
+	SET_RUNTIME_PM_OPS(zynqmp_dma_runtime_suspend,
+			   zynqmp_dma_runtime_resume, NULL)
+};
+
+/**
  * zynqmp_dma_probe - Driver probe function
  * @pdev: Pointer to the platform_device structure
  *
@@ -984,12 +1048,33 @@ static int zynqmp_dma_probe(struct platform_device *pdev)
 	p->device_config = zynqmp_dma_device_config;
 	p->dev = &pdev->dev;
 
+	zdev->clk_main = devm_clk_get(&pdev->dev, "clk_main");
+	if (IS_ERR(zdev->clk_main)) {
+		dev_err(&pdev->dev, "main clock not found.\n");
+		return PTR_ERR(zdev->clk_main);
+	}
+
+	zdev->clk_apb = devm_clk_get(&pdev->dev, "clk_apb");
+	if (IS_ERR(zdev->clk_apb)) {
+		dev_err(&pdev->dev, "apb clock not found.\n");
+		return PTR_ERR(zdev->clk_apb);
+	}
+
 	platform_set_drvdata(pdev, zdev);
+	pm_runtime_set_autosuspend_delay(zdev->dev, ZDMA_PM_TIMEOUT);
+	pm_runtime_use_autosuspend(zdev->dev);
+	pm_runtime_enable(zdev->dev);
+	pm_runtime_get_sync(zdev->dev);
+	if (!pm_runtime_enabled(zdev->dev)) {
+		ret = zynqmp_dma_runtime_resume(zdev->dev);
+		if (ret)
+			return ret;
+	}
 
 	ret = zynqmp_dma_chan_probe(zdev, pdev);
 	if (ret) {
 		dev_err(&pdev->dev, "Probing channel failed\n");
-		goto free_chan_resources;
+		goto err_disable_pm;
 	}
 
 	p->dst_addr_widths = BIT(zdev->chan->bus_width / 8);
@@ -1005,12 +1090,19 @@ static int zynqmp_dma_probe(struct platform_device *pdev)
 		goto free_chan_resources;
 	}
 
+	pm_runtime_mark_last_busy(zdev->dev);
+	pm_runtime_put_sync_autosuspend(zdev->dev);
+
 	dev_info(&pdev->dev, "ZynqMP DMA driver Probe success\n");
 
 	return 0;
 
 free_chan_resources:
 	zynqmp_dma_chan_remove(zdev->chan);
+err_disable_pm:
+	if (!pm_runtime_enabled(zdev->dev))
+		zynqmp_dma_runtime_suspend(zdev->dev);
+	pm_runtime_disable(zdev->dev);
 	return ret;
 }
 
@@ -1028,6 +1120,9 @@ static int zynqmp_dma_remove(struct platform_device *pdev)
 	dma_async_device_unregister(&zdev->common);
 
 	zynqmp_dma_chan_remove(zdev->chan);
+	pm_runtime_disable(zdev->dev);
+	if (!pm_runtime_enabled(zdev->dev))
+		zynqmp_dma_runtime_suspend(zdev->dev);
 
 	return 0;
 }
@@ -1042,6 +1137,7 @@ static struct platform_driver zynqmp_dma_driver = {
 	.driver = {
 		.name = "xilinx-zynqmp-dma",
 		.of_match_table = zynqmp_dma_of_match,
+		.pm = &zynqmp_dma_dev_pm_ops,
 	},
 	.probe = zynqmp_dma_probe,
 	.remove = zynqmp_dma_remove,
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 4/4] dmaengine: zynqmp_dma: Fix race condition in the probe
From: Kedareswara rao Appana @ 2017-12-07  5:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1512624268-13944-1-git-send-email-appanad@xilinx.com>

Incase of interrupt property is not present,
Driver is trying to free an invalid irq,
This patch fixes it by adding a check before freeing the irq.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v2:
--> New patch.

 drivers/dma/xilinx/zynqmp_dma.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
index a297a26..f146458 100644
--- a/drivers/dma/xilinx/zynqmp_dma.c
+++ b/drivers/dma/xilinx/zynqmp_dma.c
@@ -851,7 +851,8 @@ static void zynqmp_dma_chan_remove(struct zynqmp_dma_chan *chan)
 	if (!chan)
 		return;
 
-	devm_free_irq(chan->zdev->dev, chan->irq, chan);
+	if (chan->irq)
+		devm_free_irq(chan->zdev->dev, chan->irq, chan);
 	tasklet_kill(&chan->tasklet);
 	list_del(&chan->common.device_node);
 }
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 3/4] dmaengine: zynqmp_dma: Fix issues with overflow interrupt
From: Kedareswara rao Appana @ 2017-12-07  5:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1512624268-13944-1-git-send-email-appanad@xilinx.com>

This patch fixes the below issues.
--> Need to clear the channel data count register
when overflow interrupts occurs.
--> Reduce the log level from _info to _dbg when
overflow interrupt occurs.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v2:
--> None.

 drivers/dma/xilinx/zynqmp_dma.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
index b9b5c0a..a297a26 100644
--- a/drivers/dma/xilinx/zynqmp_dma.c
+++ b/drivers/dma/xilinx/zynqmp_dma.c
@@ -48,6 +48,7 @@
 #define ZYNQMP_DMA_SRC_START_MSB	0x15C
 #define ZYNQMP_DMA_DST_START_LSB	0x160
 #define ZYNQMP_DMA_DST_START_MSB	0x164
+#define ZYNQMP_DMA_TOTAL_BYTE		0x188
 #define ZYNQMP_DMA_RATE_CTRL		0x18C
 #define ZYNQMP_DMA_IRQ_SRC_ACCT		0x190
 #define ZYNQMP_DMA_IRQ_DST_ACCT		0x194
@@ -513,6 +514,7 @@ static int zynqmp_dma_alloc_chan_resources(struct dma_chan *dchan)
 static void zynqmp_dma_start(struct zynqmp_dma_chan *chan)
 {
 	writel(ZYNQMP_DMA_INT_EN_DEFAULT_MASK, chan->regs + ZYNQMP_DMA_IER);
+	writel(0, chan->regs + ZYNQMP_DMA_TOTAL_BYTE);
 	chan->idle = false;
 	writel(ZYNQMP_DMA_ENABLE, chan->regs + ZYNQMP_DMA_CTRL2);
 }
@@ -524,6 +526,8 @@ static void zynqmp_dma_start(struct zynqmp_dma_chan *chan)
  */
 static void zynqmp_dma_handle_ovfl_int(struct zynqmp_dma_chan *chan, u32 status)
 {
+	if (status & ZYNQMP_DMA_BYTE_CNT_OVRFL)
+		writel(0, chan->regs + ZYNQMP_DMA_TOTAL_BYTE);
 	if (status & ZYNQMP_DMA_IRQ_DST_ACCT_ERR)
 		readl(chan->regs + ZYNQMP_DMA_IRQ_DST_ACCT);
 	if (status & ZYNQMP_DMA_IRQ_SRC_ACCT_ERR)
@@ -724,7 +728,7 @@ static irqreturn_t zynqmp_dma_irq_handler(int irq, void *data)
 
 	if (status & ZYNQMP_DMA_INT_OVRFL) {
 		zynqmp_dma_handle_ovfl_int(chan, status);
-		dev_info(chan->dev, "Channel %p overflow interrupt\n", chan);
+		dev_dbg(chan->dev, "Channel %p overflow interrupt\n", chan);
 		ret = IRQ_HANDLED;
 	}
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 2/4] dmaengine: zynqmp_dma: Fix warning variable 'val' set but not used
From: Kedareswara rao Appana @ 2017-12-07  5:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1512624268-13944-1-git-send-email-appanad@xilinx.com>

This patch fixes the below warning

drivers/dma/xilinx/zynqmp_dma.c: In function 'zynqmp_dma_handle_ovfl_int':
drivers/dma/xilinx/zynqmp_dma.c:522:6: warning: variable 'val' set but not used [-Wunused-but-set-variable]

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v2:
--> None.

 drivers/dma/xilinx/zynqmp_dma.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
index 4376e4a..b9b5c0a 100644
--- a/drivers/dma/xilinx/zynqmp_dma.c
+++ b/drivers/dma/xilinx/zynqmp_dma.c
@@ -524,12 +524,10 @@ static void zynqmp_dma_start(struct zynqmp_dma_chan *chan)
  */
 static void zynqmp_dma_handle_ovfl_int(struct zynqmp_dma_chan *chan, u32 status)
 {
-	u32 val;
-
 	if (status & ZYNQMP_DMA_IRQ_DST_ACCT_ERR)
-		val = readl(chan->regs + ZYNQMP_DMA_IRQ_DST_ACCT);
+		readl(chan->regs + ZYNQMP_DMA_IRQ_DST_ACCT);
 	if (status & ZYNQMP_DMA_IRQ_SRC_ACCT_ERR)
-		val = readl(chan->regs + ZYNQMP_DMA_IRQ_SRC_ACCT);
+		readl(chan->regs + ZYNQMP_DMA_IRQ_SRC_ACCT);
 }
 
 static void zynqmp_dma_config(struct zynqmp_dma_chan *chan)
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 1/4] dmaengine: zynqmp_dma: Fix kernel doc-format
From: Kedareswara rao Appana @ 2017-12-07  5:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1512624268-13944-1-git-send-email-appanad@xilinx.com>

This patch fixes the below kernel doc warnings
drivers/dma/xilinx/zynqmp_dma.c:552: info: Scanning doc for
zynqmp_dma_device_config
drivers/dma/xilinx/zynqmp_dma.c:558: warning: No description found for
return value of 'zynqmp_dma_device_config'
drivers/dma/xilinx/zynqmp_dma.c:649: info: Scanning doc for
zynqmp_dma_free_descriptors
drivers/dma/xilinx/zynqmp_dma.c:653: warning: No description found for
parameter 'chan'
drivers/dma/xilinx/zynqmp_dma.c:653: warning: Excess function parameter
'dchan' description in 'zynqmp_dma_free_descriptors'

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v2:
--> None.

 drivers/dma/xilinx/zynqmp_dma.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
index 4fa14bf..4376e4a 100644
--- a/drivers/dma/xilinx/zynqmp_dma.c
+++ b/drivers/dma/xilinx/zynqmp_dma.c
@@ -552,6 +552,8 @@ static void zynqmp_dma_config(struct zynqmp_dma_chan *chan)
  * zynqmp_dma_device_config - Zynqmp dma device configuration
  * @dchan: DMA channel
  * @config: DMA device config
+ *
+ * Return: 0 always
  */
 static int zynqmp_dma_device_config(struct dma_chan *dchan,
 				    struct dma_slave_config *config)
@@ -647,7 +649,7 @@ static void zynqmp_dma_issue_pending(struct dma_chan *dchan)
 
 /**
  * zynqmp_dma_free_descriptors - Free channel descriptors
- * @dchan: DMA channel pointer
+ * @chan: ZynqMP DMA channel pointer
  */
 static void zynqmp_dma_free_descriptors(struct zynqmp_dma_chan *chan)
 {
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 0/4] dmaengine: zynqmp_dma: Bug fixes
From: Kedareswara rao Appana @ 2017-12-07  5:24 UTC (permalink / raw)
  To: linux-arm-kernel

This patch series does the below
--> Fixes kernel doc format style issues.
--> Fixes warings in the driver.
--> Fixes issues with overflow interrupt.
--> Fixes race condition in the probe.

This patch series got created on top of the below patch
"dmaengine: zynqmp_dma: Add runtime pm support"

Changes for v2:
--> New patch added "dmaengine: zynqmp_dma: Fix race condition in the probe"
to the series.

Kedareswara rao Appana (4):
  dmaengine: zynqmp_dma: Fix kernel doc-format
  dmaengine: zynqmp_dma: Fix warning variable 'val' set but not used
  dmaengine: zynqmp_dma: Fix issues with overflow interrupt
  dmaengine: zynqmp_dma: Fix race condition in the probe

 drivers/dma/xilinx/zynqmp_dma.c | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

-- 
2.7.4

^ permalink raw reply

* [PATCH v7 6/6] dmaengine: xilinx_dma: Differentiate probe based on the ip type
From: Kedareswara rao Appana @ 2017-12-07  5:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1512624067-13554-1-git-send-email-appanad@xilinx.com>

This patch updates the probe banner info based on the ip probed.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v7:
--> New patch.

 drivers/dma/xilinx/xilinx_dma.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index b09a8ef..52e2f18 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -2680,7 +2680,12 @@ static int xilinx_dma_probe(struct platform_device *pdev)
 		goto error;
 	}
 
-	dev_info(&pdev->dev, "Xilinx AXI VDMA Engine Driver Probed!!\n");
+	if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA)
+		dev_info(&pdev->dev, "Xilinx AXI DMA Engine Driver Probed!!\n");
+	else if (xdev->dma_config->dmatype == XDMA_TYPE_CDMA)
+		dev_info(&pdev->dev, "Xilinx AXI CDMA Engine Driver Probed!!\n");
+	else
+		dev_info(&pdev->dev, "Xilinx AXI VDMA Engine Driver Probed!!\n");
 
 	return 0;
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 5/6] dmaengine: xilinx_dma: fix style issues from checkpatch
From: Kedareswara rao Appana @ 2017-12-07  5:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1512624067-13554-1-git-send-email-appanad@xilinx.com>

This patch fixes below.
ERROR: open brace '{' following function definitions go on the next line
+static int xilinx_dma_child_probe(struct xilinx_dma_device *xdev,
+				    struct device_node *node) {

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v7:
--> New patch.

 drivers/dma/xilinx/xilinx_dma.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index eaa93fe..b09a8ef 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -2484,7 +2484,8 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
  * Return: 0 always.
  */
 static int xilinx_dma_child_probe(struct xilinx_dma_device *xdev,
-				    struct device_node *node) {
+				    struct device_node *node)
+{
 	int ret, i, nr_channels = 1;
 
 	ret = of_property_read_u32(node, "dma-channels", &nr_channels);
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 4/6] dmaengine: xilinx_dma: Fix kernel doc warnings
From: Kedareswara rao Appana @ 2017-12-07  5:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1512624067-13554-1-git-send-email-appanad@xilinx.com>

This patch fixes the kernel doc warnings
in the driver.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v7:
--> New patch.

 drivers/dma/xilinx/xilinx_dma.c | 37 ++++++++++++++++++++++++-------------
 1 file changed, 24 insertions(+), 13 deletions(-)

diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index ab01306..eaa93fe 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -214,8 +214,8 @@ struct xilinx_vdma_desc_hw {
  * @next_desc_msb: MSB of Next Descriptor Pointer @0x04
  * @buf_addr: Buffer address @0x08
  * @buf_addr_msb: MSB of Buffer address @0x0C
- * @pad1: Reserved @0x10
- * @pad2: Reserved @0x14
+ * @mcdma_control: Control field for mcdma @0x10
+ * @vsize_stride: Vsize and Stride field for mcdma @0x14
  * @control: Control field @0x18
  * @status: Status field @0x1C
  * @app: APP Fields @0x20 - 0x30
@@ -235,11 +235,11 @@ struct xilinx_axidma_desc_hw {
 /**
  * struct xilinx_cdma_desc_hw - Hardware Descriptor
  * @next_desc: Next Descriptor Pointer @0x00
- * @next_descmsb: Next Descriptor Pointer MSB @0x04
+ * @next_desc_msb: Next Descriptor Pointer MSB @0x04
  * @src_addr: Source address @0x08
- * @src_addrmsb: Source address MSB @0x0C
+ * @src_addr_msb: Source address MSB @0x0C
  * @dest_addr: Destination address @0x10
- * @dest_addrmsb: Destination address MSB @0x14
+ * @dest_addr_msb: Destination address MSB @0x14
  * @control: Control field @0x18
  * @status: Status field @0x1C
  */
@@ -339,6 +339,7 @@ struct xilinx_dma_tx_descriptor {
  * @cyclic_seg_p: Physical allocated segments base for cyclic dma
  * @start_transfer: Differentiate b/w DMA IP's transfer
  * @stop_transfer: Differentiate b/w DMA IP's quiesce
+ * @tdest: TDEST value for mcdma
  */
 struct xilinx_dma_chan {
 	struct xilinx_dma_device *xdev;
@@ -378,11 +379,11 @@ struct xilinx_dma_chan {
 };
 
 /**
- * enum xdma_ip_type: DMA IP type.
+ * enum xdma_ip_type - DMA IP type.
  *
- * XDMA_TYPE_AXIDMA: Axi dma ip.
- * XDMA_TYPE_CDMA: Axi cdma ip.
- * XDMA_TYPE_VDMA: Axi vdma ip.
+ * @XDMA_TYPE_AXIDMA: Axi dma ip.
+ * @XDMA_TYPE_CDMA: Axi cdma ip.
+ * @XDMA_TYPE_VDMA: Axi vdma ip.
  *
  */
 enum xdma_ip_type {
@@ -994,6 +995,8 @@ static enum dma_status xilinx_dma_tx_status(struct dma_chan *dchan,
 /**
  * xilinx_dma_stop_transfer - Halt DMA channel
  * @chan: Driver specific DMA channel
+ *
+ * Return: '0' on success and failure value on error
  */
 static int xilinx_dma_stop_transfer(struct xilinx_dma_chan *chan)
 {
@@ -1010,6 +1013,8 @@ static int xilinx_dma_stop_transfer(struct xilinx_dma_chan *chan)
 /**
  * xilinx_cdma_stop_transfer - Wait for the current transfer to complete
  * @chan: Driver specific DMA channel
+ *
+ * Return: '0' on success and failure value on error
  */
 static int xilinx_cdma_stop_transfer(struct xilinx_dma_chan *chan)
 {
@@ -1825,11 +1830,14 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
 
 /**
  * xilinx_dma_prep_dma_cyclic - prepare descriptors for a DMA_SLAVE transaction
- * @chan: DMA channel
- * @sgl: scatterlist to transfer to/from
- * @sg_len: number of entries in @scatterlist
+ * @dchan: DMA channel
+ * @buf_addr: Physical address of the buffer
+ * @buf_len: Total length of the cyclic buffers
+ * @period_len: length of individual cyclic buffer
  * @direction: DMA direction
  * @flags: transfer ack flags
+ *
+ * Return: Async transaction descriptor on success and NULL on failure
  */
 static struct dma_async_tx_descriptor *xilinx_dma_prep_dma_cyclic(
 	struct dma_chan *dchan, dma_addr_t buf_addr, size_t buf_len,
@@ -2013,7 +2021,9 @@ xilinx_dma_prep_interleaved(struct dma_chan *dchan,
 
 /**
  * xilinx_dma_terminate_all - Halt the channel and free descriptors
- * @chan: Driver specific DMA Channel pointer
+ * @dchan: Driver specific DMA Channel pointer
+ *
+ * Return: '0' always.
  */
 static int xilinx_dma_terminate_all(struct dma_chan *dchan)
 {
@@ -2328,6 +2338,7 @@ static void xdma_disable_allclks(struct xilinx_dma_device *xdev)
  *
  * @xdev: Driver specific device structure
  * @node: Device node
+ * @chan_id: DMA Channel id
  *
  * Return: '0' on success and failure value on error
  */
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 3/6] dmaengine: xilinx_dma: Fix race condition in the driver for multiple descriptor scenario
From: Kedareswara rao Appana @ 2017-12-07  5:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1512624067-13554-1-git-send-email-appanad@xilinx.com>

As per axi dmaengine spec the software must not move the tail pointer
to a location that has not been updated (next descriptor field of the
h/w descriptor should always point to a valid address).

When user submits multiple descriptors on the recv side, with the
current driver flow the last buffer descriptor next descriptor field
points to a invalid location, resulting the invalid data or errors from the
axidma dmaengine.

This patch fixes this issue by creating a buffer descritpor chain during
channel allocation itself and use those buffer descriptors for the
subsequent dma operations.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v7:
---> None.
Changes for v6:
---> Updated Commit message as suggested by Vinod.
Changes for v5:
---> None.
Changes for v4:
---> None.
Changes for v3:
---> None.
Changes for v2:
---> None.

 drivers/dma/xilinx/xilinx_dma.c | 135 +++++++++++++++++++++++++---------------
 1 file changed, 84 insertions(+), 51 deletions(-)

diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index 9063ca0..ab01306 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -165,6 +165,7 @@
 #define XILINX_DMA_BD_SOP		BIT(27)
 #define XILINX_DMA_BD_EOP		BIT(26)
 #define XILINX_DMA_COALESCE_MAX		255
+#define XILINX_DMA_NUM_DESCS		255
 #define XILINX_DMA_NUM_APP_WORDS	5
 
 /* Multi-Channel DMA Descriptor offsets*/
@@ -312,6 +313,7 @@ struct xilinx_dma_tx_descriptor {
  * @pending_list: Descriptors waiting
  * @active_list: Descriptors ready to submit
  * @done_list: Complete descriptors
+ * @free_seg_list: Free descriptors
  * @common: DMA common channel
  * @desc_pool: Descriptors pool
  * @dev: The dma device
@@ -332,7 +334,9 @@ struct xilinx_dma_tx_descriptor {
  * @desc_submitcount: Descriptor h/w submitted count
  * @residue: Residue for AXI DMA
  * @seg_v: Statically allocated segments base
+ * @seg_p: Physical allocated segments base
  * @cyclic_seg_v: Statically allocated segment base for cyclic transfers
+ * @cyclic_seg_p: Physical allocated segments base for cyclic dma
  * @start_transfer: Differentiate b/w DMA IP's transfer
  * @stop_transfer: Differentiate b/w DMA IP's quiesce
  */
@@ -344,6 +348,7 @@ struct xilinx_dma_chan {
 	struct list_head pending_list;
 	struct list_head active_list;
 	struct list_head done_list;
+	struct list_head free_seg_list;
 	struct dma_chan common;
 	struct dma_pool *desc_pool;
 	struct device *dev;
@@ -364,7 +369,9 @@ struct xilinx_dma_chan {
 	u32 desc_submitcount;
 	u32 residue;
 	struct xilinx_axidma_tx_segment *seg_v;
+	dma_addr_t seg_p;
 	struct xilinx_axidma_tx_segment *cyclic_seg_v;
+	dma_addr_t cyclic_seg_p;
 	void (*start_transfer)(struct xilinx_dma_chan *chan);
 	int (*stop_transfer)(struct xilinx_dma_chan *chan);
 	u16 tdest;
@@ -584,18 +591,32 @@ xilinx_cdma_alloc_tx_segment(struct xilinx_dma_chan *chan)
 static struct xilinx_axidma_tx_segment *
 xilinx_axidma_alloc_tx_segment(struct xilinx_dma_chan *chan)
 {
-	struct xilinx_axidma_tx_segment *segment;
-	dma_addr_t phys;
-
-	segment = dma_pool_zalloc(chan->desc_pool, GFP_ATOMIC, &phys);
-	if (!segment)
-		return NULL;
+	struct xilinx_axidma_tx_segment *segment = NULL;
+	unsigned long flags;
 
-	segment->phys = phys;
+	spin_lock_irqsave(&chan->lock, flags);
+	if (!list_empty(&chan->free_seg_list)) {
+		segment = list_first_entry(&chan->free_seg_list,
+					   struct xilinx_axidma_tx_segment,
+					   node);
+		list_del(&segment->node);
+	}
+	spin_unlock_irqrestore(&chan->lock, flags);
 
 	return segment;
 }
 
+static void xilinx_dma_clean_hw_desc(struct xilinx_axidma_desc_hw *hw)
+{
+	u32 next_desc = hw->next_desc;
+	u32 next_desc_msb = hw->next_desc_msb;
+
+	memset(hw, 0, sizeof(struct xilinx_axidma_desc_hw));
+
+	hw->next_desc = next_desc;
+	hw->next_desc_msb = next_desc_msb;
+}
+
 /**
  * xilinx_dma_free_tx_segment - Free transaction segment
  * @chan: Driver specific DMA channel
@@ -604,7 +625,9 @@ xilinx_axidma_alloc_tx_segment(struct xilinx_dma_chan *chan)
 static void xilinx_dma_free_tx_segment(struct xilinx_dma_chan *chan,
 				struct xilinx_axidma_tx_segment *segment)
 {
-	dma_pool_free(chan->desc_pool, segment, segment->phys);
+	xilinx_dma_clean_hw_desc(&segment->hw);
+
+	list_add_tail(&segment->node, &chan->free_seg_list);
 }
 
 /**
@@ -729,16 +752,26 @@ static void xilinx_dma_free_descriptors(struct xilinx_dma_chan *chan)
 static void xilinx_dma_free_chan_resources(struct dma_chan *dchan)
 {
 	struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
+	unsigned long flags;
 
 	dev_dbg(chan->dev, "Free all channel resources.\n");
 
 	xilinx_dma_free_descriptors(chan);
+
 	if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
-		xilinx_dma_free_tx_segment(chan, chan->cyclic_seg_v);
-		xilinx_dma_free_tx_segment(chan, chan->seg_v);
+		spin_lock_irqsave(&chan->lock, flags);
+		INIT_LIST_HEAD(&chan->free_seg_list);
+		spin_unlock_irqrestore(&chan->lock, flags);
+
+		/* Free Memory that is allocated for cyclic DMA Mode */
+		dma_free_coherent(chan->dev, sizeof(*chan->cyclic_seg_v),
+				  chan->cyclic_seg_v, chan->cyclic_seg_p);
+	}
+
+	if (chan->xdev->dma_config->dmatype != XDMA_TYPE_AXIDMA) {
+		dma_pool_destroy(chan->desc_pool);
+		chan->desc_pool = NULL;
 	}
-	dma_pool_destroy(chan->desc_pool);
-	chan->desc_pool = NULL;
 }
 
 /**
@@ -821,6 +854,7 @@ static void xilinx_dma_do_tasklet(unsigned long data)
 static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
 {
 	struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
+	int i;
 
 	/* Has this channel already been allocated? */
 	if (chan->desc_pool)
@@ -831,11 +865,30 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
 	 * for meeting Xilinx VDMA specification requirement.
 	 */
 	if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
-		chan->desc_pool = dma_pool_create("xilinx_dma_desc_pool",
-				   chan->dev,
-				   sizeof(struct xilinx_axidma_tx_segment),
-				   __alignof__(struct xilinx_axidma_tx_segment),
-				   0);
+		/* Allocate the buffer descriptors. */
+		chan->seg_v = dma_zalloc_coherent(chan->dev,
+						  sizeof(*chan->seg_v) *
+						  XILINX_DMA_NUM_DESCS,
+						  &chan->seg_p, GFP_KERNEL);
+		if (!chan->seg_v) {
+			dev_err(chan->dev,
+				"unable to allocate channel %d descriptors\n",
+				chan->id);
+			return -ENOMEM;
+		}
+
+		for (i = 0; i < XILINX_DMA_NUM_DESCS; i++) {
+			chan->seg_v[i].hw.next_desc =
+			lower_32_bits(chan->seg_p + sizeof(*chan->seg_v) *
+				((i + 1) % XILINX_DMA_NUM_DESCS));
+			chan->seg_v[i].hw.next_desc_msb =
+			upper_32_bits(chan->seg_p + sizeof(*chan->seg_v) *
+				((i + 1) % XILINX_DMA_NUM_DESCS));
+			chan->seg_v[i].phys = chan->seg_p +
+				sizeof(*chan->seg_v) * i;
+			list_add_tail(&chan->seg_v[i].node,
+				      &chan->free_seg_list);
+		}
 	} else if (chan->xdev->dma_config->dmatype == XDMA_TYPE_CDMA) {
 		chan->desc_pool = dma_pool_create("xilinx_cdma_desc_pool",
 				   chan->dev,
@@ -850,7 +903,8 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
 				     0);
 	}
 
-	if (!chan->desc_pool) {
+	if (!chan->desc_pool &&
+	    (chan->xdev->dma_config->dmatype != XDMA_TYPE_AXIDMA)) {
 		dev_err(chan->dev,
 			"unable to allocate channel %d descriptor pool\n",
 			chan->id);
@@ -859,22 +913,20 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
 
 	if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
 		/*
-		 * For AXI DMA case after submitting a pending_list, keep
-		 * an extra segment allocated so that the "next descriptor"
-		 * pointer on the tail descriptor always points to a
-		 * valid descriptor, even when paused after reaching taildesc.
-		 * This way, it is possible to issue additional
-		 * transfers without halting and restarting the channel.
-		 */
-		chan->seg_v = xilinx_axidma_alloc_tx_segment(chan);
-
-		/*
 		 * For cyclic DMA mode we need to program the tail Descriptor
 		 * register with a value which is not a part of the BD chain
 		 * so allocating a desc segment during channel allocation for
 		 * programming tail descriptor.
 		 */
-		chan->cyclic_seg_v = xilinx_axidma_alloc_tx_segment(chan);
+		chan->cyclic_seg_v = dma_zalloc_coherent(chan->dev,
+					sizeof(*chan->cyclic_seg_v),
+					&chan->cyclic_seg_p, GFP_KERNEL);
+		if (!chan->cyclic_seg_v) {
+			dev_err(chan->dev,
+				"unable to allocate desc segment for cyclic DMA\n");
+			return -ENOMEM;
+		}
+		chan->cyclic_seg_v->phys = chan->cyclic_seg_p;
 	}
 
 	dma_cookie_init(dchan);
@@ -1184,7 +1236,7 @@ static void xilinx_cdma_start_transfer(struct xilinx_dma_chan *chan)
 static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 {
 	struct xilinx_dma_tx_descriptor *head_desc, *tail_desc;
-	struct xilinx_axidma_tx_segment *tail_segment, *old_head, *new_head;
+	struct xilinx_axidma_tx_segment *tail_segment;
 	u32 reg;
 
 	if (chan->err)
@@ -1203,21 +1255,6 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 	tail_segment = list_last_entry(&tail_desc->segments,
 				       struct xilinx_axidma_tx_segment, node);
 
-	if (chan->has_sg && !chan->xdev->mcdma) {
-		old_head = list_first_entry(&head_desc->segments,
-					struct xilinx_axidma_tx_segment, node);
-		new_head = chan->seg_v;
-		/* Copy Buffer Descriptor fields. */
-		new_head->hw = old_head->hw;
-
-		/* Swap and save new reserve */
-		list_replace_init(&old_head->node, &new_head->node);
-		chan->seg_v = old_head;
-
-		tail_segment->hw.next_desc = chan->seg_v->phys;
-		head_desc->async_tx.phys = new_head->phys;
-	}
-
 	reg = dma_ctrl_read(chan, XILINX_DMA_REG_DMACR);
 
 	if (chan->desc_pendingcount <= XILINX_DMA_COALESCE_MAX) {
@@ -1705,7 +1742,7 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
 {
 	struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
 	struct xilinx_dma_tx_descriptor *desc;
-	struct xilinx_axidma_tx_segment *segment = NULL, *prev = NULL;
+	struct xilinx_axidma_tx_segment *segment = NULL;
 	u32 *app_w = (u32 *)context;
 	struct scatterlist *sg;
 	size_t copy;
@@ -1756,10 +1793,6 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
 					       XILINX_DMA_NUM_APP_WORDS);
 			}
 
-			if (prev)
-				prev->hw.next_desc = segment->phys;
-
-			prev = segment;
 			sg_used += copy;
 
 			/*
@@ -1773,7 +1806,6 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
 	segment = list_first_entry(&desc->segments,
 				   struct xilinx_axidma_tx_segment, node);
 	desc->async_tx.phys = segment->phys;
-	prev->hw.next_desc = segment->phys;
 
 	/* For the last DMA_MEM_TO_DEV transfer, set EOP */
 	if (chan->direction == DMA_MEM_TO_DEV) {
@@ -2328,6 +2360,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 	INIT_LIST_HEAD(&chan->pending_list);
 	INIT_LIST_HEAD(&chan->done_list);
 	INIT_LIST_HEAD(&chan->active_list);
+	INIT_LIST_HEAD(&chan->free_seg_list);
 
 	/* Retrieve the channel properties from the device tree */
 	has_dre = of_property_read_bool(node, "xlnx,include-dre");
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 2/6] dmaeninge: xilinx_dma: Fix bug in multiple frame stores scenario in vdma
From: Kedareswara rao Appana @ 2017-12-07  5:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1512624067-13554-1-git-send-email-appanad@xilinx.com>

VDMA engine default frame buffer configuration is cirular mode.
in this mode dmaengine continuously circles through h/w configured fstore
frame buffers.

When vdma h/w is configured for more than one frame.
for example h/w is configured for n number of frames, user
submits less than n number of frames and triggered the dmaengine
using issue_pending API.

since the h/w (or) driver default configuraiton is circular mode
h/w tries to write/read from an invalid frame buffer resulting
errors from the vdma dmaengine.

This patch fixes this issue by enabling the park mode as
default mode configuration for frame buffers in s/w,
so that driver can handle all cases for "k" frames where n%k==0
(n is a multiple of k) by simply replicating the frame pointers.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v7:
---> Used park mode as default configuration as suggested
by Mike Looijmans.
---> Updated commit message as suggested by Vinod(No need to
start each line with Title cases).
Changes for v6:
---> Added Rob Acked-by
---> Updated commit message as suggested by Vinod.
Changes for v5:
---> Updated xlnx,fstore-config property to xlnx,fstore-enable
     and updated description as suggested by Rob.
Changes for v4:
---> Add Check for framestore configuration on Transmit case as well
     as suggested by Jose Abreu.
---> Modified the dev_dbg checks to dev_warn checks as suggested
     by Jose Abreu.
Changes for v3:
---> Added Checks for frame store configuration. If frame store
     Configuration is not present at the h/w level and user
     Submits less frames added debug prints in the driver as relevant.
Changes for v2:
---> Fixed race conditions in the driver as suggested by Jose Abreu
---> Fixed unnecessray if else checks in the vdma_start_transfer
     as suggested by Laurent Pinchart.

 drivers/dma/xilinx/xilinx_dma.c | 41 +++++++++++++++++++----------------------
 1 file changed, 19 insertions(+), 22 deletions(-)

diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index c246563..9063ca0 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -99,7 +99,9 @@
 #define XILINX_DMA_REG_FRMPTR_STS		0x0024
 #define XILINX_DMA_REG_PARK_PTR		0x0028
 #define XILINX_DMA_PARK_PTR_WR_REF_SHIFT	8
+#define XILINX_DMA_PARK_PTR_WR_REF_MASK		GENMASK(12, 8)
 #define XILINX_DMA_PARK_PTR_RD_REF_SHIFT	0
+#define XILINX_DMA_PARK_PTR_RD_REF_MASK		GENMASK(4, 0)
 #define XILINX_DMA_REG_VDMA_VERSION		0x002c
 
 /* Register Direct Mode Registers */
@@ -998,7 +1000,7 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 {
 	struct xilinx_vdma_config *config = &chan->config;
 	struct xilinx_dma_tx_descriptor *desc, *tail_desc;
-	u32 reg;
+	u32 reg, j;
 	struct xilinx_vdma_tx_segment *tail_segment;
 
 	/* This function was invoked with lock held */
@@ -1035,10 +1037,6 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 	else
 		reg &= ~XILINX_DMA_DMACR_FRAMECNT_EN;
 
-	/* Configure channel to allow number frame buffers */
-	dma_ctrl_write(chan, XILINX_DMA_REG_FRMSTORE,
-			chan->desc_pendingcount);
-
 	/*
 	 * With SG, start with circular mode, so that BDs can be fetched.
 	 * In direct register mode, if not parking, enable circular mode
@@ -1051,17 +1049,16 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 
 	dma_ctrl_write(chan, XILINX_DMA_REG_DMACR, reg);
 
-	if (config->park && (config->park_frm >= 0) &&
-			(config->park_frm < chan->num_frms)) {
-		if (chan->direction == DMA_MEM_TO_DEV)
-			dma_write(chan, XILINX_DMA_REG_PARK_PTR,
-				config->park_frm <<
-					XILINX_DMA_PARK_PTR_RD_REF_SHIFT);
-		else
-			dma_write(chan, XILINX_DMA_REG_PARK_PTR,
-				config->park_frm <<
-					XILINX_DMA_PARK_PTR_WR_REF_SHIFT);
+	j = chan->desc_submitcount;
+	reg = dma_read(chan, XILINX_DMA_REG_PARK_PTR);
+	if (chan->direction == DMA_MEM_TO_DEV) {
+		reg &= ~XILINX_DMA_PARK_PTR_RD_REF_MASK;
+		reg |= j << XILINX_DMA_PARK_PTR_RD_REF_SHIFT;
+	} else {
+		reg &= ~XILINX_DMA_PARK_PTR_WR_REF_MASK;
+		reg |= j << XILINX_DMA_PARK_PTR_WR_REF_SHIFT;
 	}
+	dma_write(chan, XILINX_DMA_REG_PARK_PTR, reg);
 
 	/* Start the hardware */
 	xilinx_dma_start(chan);
@@ -1073,6 +1070,8 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 	if (chan->has_sg) {
 		dma_ctrl_write(chan, XILINX_DMA_REG_TAILDESC,
 				tail_segment->phys);
+		list_splice_tail_init(&chan->pending_list, &chan->active_list);
+		chan->desc_pendingcount = 0;
 	} else {
 		struct xilinx_vdma_tx_segment *segment, *last = NULL;
 		int i = 0;
@@ -1102,18 +1101,13 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 		vdma_desc_write(chan, XILINX_DMA_REG_FRMDLY_STRIDE,
 				last->hw.stride);
 		vdma_desc_write(chan, XILINX_DMA_REG_VSIZE, last->hw.vsize);
-	}
 
-	if (!chan->has_sg) {
-		list_del(&desc->node);
-		list_add_tail(&desc->node, &chan->active_list);
 		chan->desc_submitcount++;
 		chan->desc_pendingcount--;
+		list_del(&desc->node);
+		list_add_tail(&desc->node, &chan->active_list);
 		if (chan->desc_submitcount == chan->num_frms)
 			chan->desc_submitcount = 0;
-	} else {
-		list_splice_tail_init(&chan->pending_list, &chan->active_list);
-		chan->desc_pendingcount = 0;
 	}
 
 	chan->idle = false;
@@ -1364,6 +1358,7 @@ static int xilinx_dma_reset(struct xilinx_dma_chan *chan)
 
 	chan->err = false;
 	chan->idle = true;
+	chan->desc_submitcount = 0;
 
 	return err;
 }
@@ -2363,6 +2358,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 		chan->ctrl_offset = XILINX_DMA_MM2S_CTRL_OFFSET;
 		if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
 			chan->desc_offset = XILINX_VDMA_MM2S_DESC_OFFSET;
+			chan->config.park = 1;
 
 			if (xdev->flush_on_fsync == XILINX_DMA_FLUSH_BOTH ||
 			    xdev->flush_on_fsync == XILINX_DMA_FLUSH_MM2S)
@@ -2379,6 +2375,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 		chan->ctrl_offset = XILINX_DMA_S2MM_CTRL_OFFSET;
 		if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
 			chan->desc_offset = XILINX_VDMA_S2MM_DESC_OFFSET;
+			chan->config.park = 1;
 
 			if (xdev->flush_on_fsync == XILINX_DMA_FLUSH_BOTH ||
 			    xdev->flush_on_fsync == XILINX_DMA_FLUSH_S2MM)
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 1/6] dmaengine: xilinx_dma: Check for channel idle state before submitting dma descriptor
From: Kedareswara rao Appana @ 2017-12-07  5:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1512624067-13554-1-git-send-email-appanad@xilinx.com>

Add variable for checking channel idle state to ensure that dma
descriptor is not submitted when dmaengine is in progress.

This will avoid the polling for a bit in the status register to know
dma state in the driver hot path.

Reviewed-by: Jose Abreu <joabreu@synopsys.com>
Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
Changes for v7:
---> None.
Changes for v6:
---> Updated commit message as suggested by Vinod.
---> Added Channel idle variable description in the driver
     as suggested by Vinod.
Changes for v5:
---> None.
Changes for v4:
---> None.
Changes for v3:
---> None.
Changes for v2:
---> Add idle check in the reset as suggested by Jose Abreu
---> Removed xilinx_dma_is_running/xilinx_dma_is_idle checks
    in the driver and used common idle checks across the driver
    as suggested by Laurent Pinchart.

 drivers/dma/xilinx/xilinx_dma.c | 60 +++++++++++++++--------------------------
 1 file changed, 22 insertions(+), 38 deletions(-)

diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index 5eef133..c246563 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -321,6 +321,7 @@ struct xilinx_dma_tx_descriptor {
  * @cyclic: Check for cyclic transfers.
  * @genlock: Support genlock mode
  * @err: Channel has errors
+ * @idle: Check for channel idle
  * @tasklet: Cleanup work after irq
  * @config: Device configuration info
  * @flush_on_fsync: Flush on Frame sync
@@ -352,6 +353,7 @@ struct xilinx_dma_chan {
 	bool cyclic;
 	bool genlock;
 	bool err;
+	bool idle;
 	struct tasklet_struct tasklet;
 	struct xilinx_vdma_config config;
 	bool flush_on_fsync;
@@ -936,32 +938,6 @@ static enum dma_status xilinx_dma_tx_status(struct dma_chan *dchan,
 }
 
 /**
- * xilinx_dma_is_running - Check if DMA channel is running
- * @chan: Driver specific DMA channel
- *
- * Return: '1' if running, '0' if not.
- */
-static bool xilinx_dma_is_running(struct xilinx_dma_chan *chan)
-{
-	return !(dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) &
-		 XILINX_DMA_DMASR_HALTED) &&
-		(dma_ctrl_read(chan, XILINX_DMA_REG_DMACR) &
-		 XILINX_DMA_DMACR_RUNSTOP);
-}
-
-/**
- * xilinx_dma_is_idle - Check if DMA channel is idle
- * @chan: Driver specific DMA channel
- *
- * Return: '1' if idle, '0' if not.
- */
-static bool xilinx_dma_is_idle(struct xilinx_dma_chan *chan)
-{
-	return dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) &
-		XILINX_DMA_DMASR_IDLE;
-}
-
-/**
  * xilinx_dma_stop_transfer - Halt DMA channel
  * @chan: Driver specific DMA channel
  */
@@ -1029,6 +1005,9 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 	if (chan->err)
 		return;
 
+	if (!chan->idle)
+		return;
+
 	if (list_empty(&chan->pending_list))
 		return;
 
@@ -1040,13 +1019,6 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 	tail_segment = list_last_entry(&tail_desc->segments,
 				       struct xilinx_vdma_tx_segment, node);
 
-	/* If it is SG mode and hardware is busy, cannot submit */
-	if (chan->has_sg && xilinx_dma_is_running(chan) &&
-	    !xilinx_dma_is_idle(chan)) {
-		dev_dbg(chan->dev, "DMA controller still busy\n");
-		return;
-	}
-
 	/*
 	 * If hardware is idle, then all descriptors on the running lists are
 	 * done, start new transfers
@@ -1143,6 +1115,8 @@ static void xilinx_vdma_start_transfer(struct xilinx_dma_chan *chan)
 		list_splice_tail_init(&chan->pending_list, &chan->active_list);
 		chan->desc_pendingcount = 0;
 	}
+
+	chan->idle = false;
 }
 
 /**
@@ -1158,6 +1132,9 @@ static void xilinx_cdma_start_transfer(struct xilinx_dma_chan *chan)
 	if (chan->err)
 		return;
 
+	if (!chan->idle)
+		return;
+
 	if (list_empty(&chan->pending_list))
 		return;
 
@@ -1203,6 +1180,7 @@ static void xilinx_cdma_start_transfer(struct xilinx_dma_chan *chan)
 
 	list_splice_tail_init(&chan->pending_list, &chan->active_list);
 	chan->desc_pendingcount = 0;
+	chan->idle = false;
 }
 
 /**
@@ -1221,12 +1199,8 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 	if (list_empty(&chan->pending_list))
 		return;
 
-	/* If it is SG mode and hardware is busy, cannot submit */
-	if (chan->has_sg && xilinx_dma_is_running(chan) &&
-	    !xilinx_dma_is_idle(chan)) {
-		dev_dbg(chan->dev, "DMA controller still busy\n");
+	if (!chan->idle)
 		return;
-	}
 
 	head_desc = list_first_entry(&chan->pending_list,
 				     struct xilinx_dma_tx_descriptor, node);
@@ -1324,6 +1298,7 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 
 	list_splice_tail_init(&chan->pending_list, &chan->active_list);
 	chan->desc_pendingcount = 0;
+	chan->idle = false;
 }
 
 /**
@@ -1388,6 +1363,7 @@ static int xilinx_dma_reset(struct xilinx_dma_chan *chan)
 	}
 
 	chan->err = false;
+	chan->idle = true;
 
 	return err;
 }
@@ -1469,6 +1445,7 @@ static irqreturn_t xilinx_dma_irq_handler(int irq, void *data)
 	if (status & XILINX_DMA_DMASR_FRM_CNT_IRQ) {
 		spin_lock(&chan->lock);
 		xilinx_dma_complete_descriptor(chan);
+		chan->idle = true;
 		chan->start_transfer(chan);
 		spin_unlock(&chan->lock);
 	}
@@ -2029,6 +2006,7 @@ static int xilinx_dma_terminate_all(struct dma_chan *dchan)
 
 	/* Remove and free all of the descriptors in the lists */
 	xilinx_dma_free_descriptors(chan);
+	chan->idle = true;
 
 	if (chan->cyclic) {
 		reg = dma_ctrl_read(chan, XILINX_DMA_REG_DMACR);
@@ -2344,6 +2322,12 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 	chan->has_sg = xdev->has_sg;
 	chan->desc_pendingcount = 0x0;
 	chan->ext_addr = xdev->ext_addr;
+	/* This variable enusres that descripotrs are not
+	 * Submited when dma engine is in progress. This variable is
+	 * Added to avoid pollling for a bit in the status register to
+	 * Know dma state in the driver hot path.
+	 */
+	chan->idle = true;
 
 	spin_lock_init(&chan->lock);
 	INIT_LIST_HEAD(&chan->pending_list);
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 0/6] dmaengine: xilinx_dma: Bug fixes
From: Kedareswara rao Appana @ 2017-12-07  5:21 UTC (permalink / raw)
  To: linux-arm-kernel

This patch series fixes below bugs in DMA and VDMA IP's
---> Added channel idle checks in the driver before submitting the buffer descriptor to h/w.
---> Fixes bug in Multi frame sotres handling in VDMA
---> Fixes issues w.r.to multi frame descriptors submit with AXI DMA S2MM(recv) Side.
---> Fixed kernel doc warnings in the driver.
---> Fixed checkpatch errors in the driver.

Kedareswara rao Appana (6):
  dmaengine: xilinx_dma: Check for channel idle state before submitting
    dma descriptor
  dmaeninge: xilinx_dma: Fix bug in multiple frame stores scenario in
    vdma
  dmaengine: xilinx_dma: Fix race condition in the driver for multiple
    descriptor scenario
  dmaengine: xilinx_dma: Fix kernel doc warnings
  dmaengine: xilinx_dma: fix style issues from checkpatch
  dmaengine: xilinx_dma: Differentiate probe based on the ip type

 drivers/dma/xilinx/xilinx_dma.c | 283 ++++++++++++++++++++++------------------
 1 file changed, 157 insertions(+), 126 deletions(-)

-- 
2.7.4

^ permalink raw reply

* [PATCH v3 10/15] ARM: dts: sun8i: a83t: Add display pipeline
From: Chen-Yu Tsai @ 2017-12-07  4:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <97d3cee9d0d7a92893f646d72643bac520de5f05.1512486553.git-series.maxime.ripard@free-electrons.com>

On Tue, Dec 5, 2017 at 11:10 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> The display pipeline on the A83T is mainly composed of the mixers and
> TCONs, plus various encoders.
>
> Let's add the first mixer and TCON to the DTSI since the only board I have
> can use only the LVDS output on the first TCON. The other parts will be
> added eventually.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Reviewed-by: Chen-Yu Tsai <wens@csie.org>

^ permalink raw reply

* [PATCH v3 03/15] dt-bindings: display: sun4i-drm: Add LVDS properties
From: Chen-Yu Tsai @ 2017-12-07  3:57 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <17851ba6277b69aa9cd81de5eead62bfed271661.1512486553.git-series.maxime.ripard@free-electrons.com>

On Tue, Dec 5, 2017 at 11:10 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> Some clocks and resets supposed to drive the LVDS logic in the display
> engine have been overlooked when the driver was first introduced.
>
> Add those additional resources to the binding, and we'll deal with the ABI
> stability in the code.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 8 +++++++-
>  1 file changed, 8 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
> index 50cc72ee1168..d4259a4f5171 100644
> --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
> +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
> @@ -121,6 +121,14 @@ Required properties:
>  On SoCs other than the A33 and V3s, there is one more clock required:
>     - 'tcon-ch1': The clock driving the TCON channel 1
>
> +On SoCs that support LVDS (all SoCs but the A13, H3, H5 and V3s), you
> +need one more reset line:
> +   - 'lvds': The reset line driving the LVDS logic
> +
> +And on the SoCs newer than the A31 (sun6i and sun8i families), you
> +need one more clock line:
> +   - 'lvds-pll': The PLL that can be used to drive the LVDS clock

Is this referring to TCON0_LVDS_Clk_Sel, which can use the MIPI PLL
on the A33? Maybe the description should be more clear, like:

  - 'lvds-alt': An alternative clock separate from the TCON
                that can be used to drive the LVDS clock.

ChenYu

> +
>  DRC
>  ---
>
> --
> git-series 0.9.1

^ permalink raw reply

* [PATCH v3 04/15] dt-bindings: display: sun4i-drm: Add A83T pipeline
From: Chen-Yu Tsai @ 2017-12-07  3:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171206215922.vtsa4xqa2e5xz7cp@rob-hp-laptop>

On Thu, Dec 7, 2017 at 5:59 AM, Rob Herring <robh@kernel.org> wrote:
> On Tue, Dec 05, 2017 at 04:10:16PM +0100, Maxime Ripard wrote:
>> The A83T has two video pipelines in parallel that looks quite similar to
>> the other SoCs.
>>
>> The video planes are handled through a controller called the mixer, and the
>> video signal is then passed to the timing controller (TCON).
>>
>> And while there is two instances of the mixers and TCONs, they have a
>> significant number of differences. The TCONs are quite easy to deal with,
>> one is supposed to generate TV (in the broader term, so including things
>> like HDMI) signals, the other one LCD (so RGB, LVDS, DSI) signals. And
>> while they are called TCON0 and TCON1 in the A83t datasheet, newer SoCs
>> call them TCON-TV and TCON-LCD, which seems more appropriate.
>>
>> However, the mixers differ mostly by their capabilities, with some features
>> being available only in the first one, or the number of planes they expose,
>> but also through their register layout. And while the capabilities could be
>> represented as properties, the register layout differences would need to
>> express all the registers offsets as properties, which is usually quite
>> bad. Especially since documentation on that hardware block is close to
>> non-existant and we don't even have the list of all those registers in the
>> first place.
>>
>> So let's call them mixer 0 and 1 in our compatibles, even though the name
>> is pretty bad...
>>
>> At the moment, we only have tested the code on a board that has a single
>> display output, so we're leaving the tcon-tv and mixer1 out.
>>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> ---
>>  Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 3 +++
>>  1 file changed, 3 insertions(+)
>
> Reviewed-by: Rob Herring <robh@kernel.org>

Reviewed-by: Chen-Yu Tsai <wens@csie.org>

^ permalink raw reply

* [PATCH v3 14/15] ARM: dts: sun8i: a711: Reinstate the PMIC compatible
From: Chen-Yu Tsai @ 2017-12-07  3:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <25b482f19587ff195582995c11ee9d07f5bf42d2.1512486553.git-series.maxime.ripard@free-electrons.com>

On Tue, Dec 5, 2017 at 11:10 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> When we added the regulator support in commit 90c5d7cdae64 ("ARM: dts:
> sun8i: a711: Add regulator support"), we also dropped the PMIC's
> compatible. Since it's not in the PMIC DTSI, unlike most other PMIC
> DTSI, it obviously wasn't probing anymore.
>
> Re-add it so that everything works again.
>
> Fixes: 90c5d7cdae64 ("ARM: dts: sun8i: a711: Add regulator support")
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Reviewed-by: Chen-Yu Tsai <wens@csie.org>

^ permalink raw reply

* [PATCH 2/2] arm64: allwinner: a64: bananapi-m64: add usb otg
From: Chen-Yu Tsai @ 2017-12-07  3:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1512582664-24936-1-git-send-email-jagan@amarulasolutions.com>

On Thu, Dec 7, 2017 at 1:51 AM, Jagan Teki <jagannadh.teki@gmail.com> wrote:
> usb otg on bananapi-m64 has configured with USB-ID with PH9
> and USB-DRVVBUS attached with dcdc1 regulatort.

That is not how you read the schematic...

Intersecting lines that are tied together will have a dot representing
the connection. The DCDC1 line is a pull-up for the ID pin. This is very
clear because it has a resistor connected in series.

VBUS for OTG is controlled by the IC displayed to the right in the
schematic, which is powered from 5V, and controlled by the DRVVBUS
pin from the PMIC. Please take a look at how the A31/A33/A83T board
dts files represent this.

ChenYu

^ permalink raw reply

* [PATCH 1/3] extcon: usbc-cros-ec: add support to notify USB type cables.
From: Chanwoo Choi @ 2017-12-07  2:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171206111008.3079-1-enric.balletbo@collabora.com>

Hi Enric,

On 2017? 12? 06? 20:10, Enric Balletbo i Serra wrote:
> From: Benson Leung <bleung@chromium.org>
> 
> Extend the driver to notify host and device type cables and the presence
> of power.
> 
> Signed-off-by: Benson Leung <bleung@chromium.org>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> ---
>  drivers/extcon/extcon-usbc-cros-ec.c | 142 ++++++++++++++++++++++++++++++++++-
>  include/linux/mfd/cros_ec_commands.h |  17 +++++
>  2 files changed, 155 insertions(+), 4 deletions(-)

Looks good to me.
After you uses the BIT() macro as the Lee's comment on v2,
I'll merge this patch.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>

> 
> diff --git a/drivers/extcon/extcon-usbc-cros-ec.c b/drivers/extcon/extcon-usbc-cros-ec.c
> index 6187f73..6721ab0 100644
> --- a/drivers/extcon/extcon-usbc-cros-ec.c
> +++ b/drivers/extcon/extcon-usbc-cros-ec.c
> @@ -34,16 +34,26 @@ struct cros_ec_extcon_info {
>  
>  	struct notifier_block notifier;
>  
> +	unsigned int dr; /* data role */
> +	bool pr; /* power role (true if VBUS enabled) */
>  	bool dp; /* DisplayPort enabled */
>  	bool mux; /* SuperSpeed (usb3) enabled */
>  	unsigned int power_type;
>  };
>  
>  static const unsigned int usb_type_c_cable[] = {
> +	EXTCON_USB,
> +	EXTCON_USB_HOST,
>  	EXTCON_DISP_DP,
>  	EXTCON_NONE,
>  };
>  
> +enum usb_data_roles {
> +	DR_NONE,
> +	DR_HOST,
> +	DR_DEVICE,
> +};
> +
>  /**
>   * cros_ec_pd_command() - Send a command to the EC.
>   * @info: pointer to struct cros_ec_extcon_info
> @@ -150,6 +160,7 @@ static int cros_ec_usb_get_role(struct cros_ec_extcon_info *info,
>  	pd_control.port = info->port_id;
>  	pd_control.role = USB_PD_CTRL_ROLE_NO_CHANGE;
>  	pd_control.mux = USB_PD_CTRL_MUX_NO_CHANGE;
> +	pd_control.swap = USB_PD_CTRL_SWAP_NONE;
>  	ret = cros_ec_pd_command(info, EC_CMD_USB_PD_CONTROL, 1,
>  				 &pd_control, sizeof(pd_control),
>  				 &resp, sizeof(resp));
> @@ -183,11 +194,72 @@ static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info *info)
>  	return resp.num_ports;
>  }
>  
> +static const char *cros_ec_usb_role_string(unsigned int role)
> +{
> +	return role == DR_NONE ? "DISCONNECTED" :
> +		(role == DR_HOST ? "DFP" : "UFP");
> +}
> +
> +static const char *cros_ec_usb_power_type_string(unsigned int type)
> +{
> +	switch (type) {
> +	case USB_CHG_TYPE_NONE:
> +		return "USB_CHG_TYPE_NONE";
> +	case USB_CHG_TYPE_PD:
> +		return "USB_CHG_TYPE_PD";
> +	case USB_CHG_TYPE_PROPRIETARY:
> +		return "USB_CHG_TYPE_PROPRIETARY";
> +	case USB_CHG_TYPE_C:
> +		return "USB_CHG_TYPE_C";
> +	case USB_CHG_TYPE_BC12_DCP:
> +		return "USB_CHG_TYPE_BC12_DCP";
> +	case USB_CHG_TYPE_BC12_CDP:
> +		return "USB_CHG_TYPE_BC12_CDP";
> +	case USB_CHG_TYPE_BC12_SDP:
> +		return "USB_CHG_TYPE_BC12_SDP";
> +	case USB_CHG_TYPE_OTHER:
> +		return "USB_CHG_TYPE_OTHER";
> +	case USB_CHG_TYPE_VBUS:
> +		return "USB_CHG_TYPE_VBUS";
> +	case USB_CHG_TYPE_UNKNOWN:
> +		return "USB_CHG_TYPE_UNKNOWN";
> +	default:
> +		return "USB_CHG_TYPE_UNKNOWN";
> +	}
> +}
> +
> +static bool cros_ec_usb_power_type_is_wall_wart(unsigned int type,
> +						unsigned int role)
> +{
> +	switch (type) {
> +	/* FIXME : Guppy, Donnettes, and other chargers will be miscategorized
> +	 * because they identify with USB_CHG_TYPE_C, but we can't return true
> +	 * here from that code because that breaks Suzy-Q and other kinds of
> +	 * USB Type-C cables and peripherals.
> +	 */
> +	case USB_CHG_TYPE_PROPRIETARY:
> +	case USB_CHG_TYPE_BC12_DCP:
> +		return true;
> +	case USB_CHG_TYPE_PD:
> +	case USB_CHG_TYPE_C:
> +	case USB_CHG_TYPE_BC12_CDP:
> +	case USB_CHG_TYPE_BC12_SDP:
> +	case USB_CHG_TYPE_OTHER:
> +	case USB_CHG_TYPE_VBUS:
> +	case USB_CHG_TYPE_UNKNOWN:
> +	case USB_CHG_TYPE_NONE:
> +	default:
> +		return false;
> +	}
> +}
> +
>  static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info,
>  				       bool force)
>  {
>  	struct device *dev = info->dev;
>  	int role, power_type;
> +	unsigned int dr = DR_NONE;
> +	bool pr = false;
>  	bool polarity = false;
>  	bool dp = false;
>  	bool mux = false;
> @@ -206,9 +278,12 @@ static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info,
>  			dev_err(dev, "failed getting role err = %d\n", role);
>  			return role;
>  		}
> +		dev_dbg(dev, "disconnected\n");
>  	} else {
>  		int pd_mux_state;
>  
> +		dr = (role & PD_CTRL_RESP_ROLE_DATA) ? DR_HOST : DR_DEVICE;
> +		pr = (role & PD_CTRL_RESP_ROLE_POWER);
>  		pd_mux_state = cros_ec_usb_get_pd_mux_state(info);
>  		if (pd_mux_state < 0)
>  			pd_mux_state = USB_PD_MUX_USB_ENABLED;
> @@ -216,20 +291,62 @@ static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info,
>  		dp = pd_mux_state & USB_PD_MUX_DP_ENABLED;
>  		mux = pd_mux_state & USB_PD_MUX_USB_ENABLED;
>  		hpd = pd_mux_state & USB_PD_MUX_HPD_IRQ;
> -	}
>  
> -	if (force || info->dp != dp || info->mux != mux ||
> -		info->power_type != power_type) {
> +		dev_dbg(dev,
> +			"connected role 0x%x pwr type %d dr %d pr %d pol %d mux %d dp %d hpd %d\n",
> +			role, power_type, dr, pr, polarity, mux, dp, hpd);
> +	}
>  
> +	/*
> +	 * When there is no USB host (e.g. USB PD charger),
> +	 * we are not really a UFP for the AP.
> +	 */
> +	if (dr == DR_DEVICE &&
> +	    cros_ec_usb_power_type_is_wall_wart(power_type, role))
> +		dr = DR_NONE;
> +
> +	if (force || info->dr != dr || info->pr != pr || info->dp != dp ||
> +	    info->mux != mux || info->power_type != power_type) {
> +		bool host_connected = false, device_connected = false;
> +
> +		dev_dbg(dev, "Type/Role switch! type = %s role = %s\n",
> +			cros_ec_usb_power_type_string(power_type),
> +			cros_ec_usb_role_string(dr));
> +		info->dr = dr;
> +		info->pr = pr;
>  		info->dp = dp;
>  		info->mux = mux;
>  		info->power_type = power_type;
>  
> -		extcon_set_state(info->edev, EXTCON_DISP_DP, dp);
> +		if (dr == DR_DEVICE)
> +			device_connected = true;
> +		else if (dr == DR_HOST)
> +			host_connected = true;
>  
> +		extcon_set_state(info->edev, EXTCON_USB, device_connected);
> +		extcon_set_state(info->edev, EXTCON_USB_HOST, host_connected);
> +		extcon_set_state(info->edev, EXTCON_DISP_DP, dp);
> +		extcon_set_property(info->edev, EXTCON_USB,
> +				    EXTCON_PROP_USB_VBUS,
> +				    (union extcon_property_value)(int)pr);
> +		extcon_set_property(info->edev, EXTCON_USB_HOST,
> +				    EXTCON_PROP_USB_VBUS,
> +				    (union extcon_property_value)(int)pr);
> +		extcon_set_property(info->edev, EXTCON_USB,
> +				    EXTCON_PROP_USB_TYPEC_POLARITY,
> +				    (union extcon_property_value)(int)polarity);
> +		extcon_set_property(info->edev, EXTCON_USB_HOST,
> +				    EXTCON_PROP_USB_TYPEC_POLARITY,
> +				    (union extcon_property_value)(int)polarity);
>  		extcon_set_property(info->edev, EXTCON_DISP_DP,
>  				    EXTCON_PROP_USB_TYPEC_POLARITY,
>  				    (union extcon_property_value)(int)polarity);
> +		extcon_set_property(info->edev, EXTCON_USB,
> +				    EXTCON_PROP_USB_SS,
> +				    (union extcon_property_value)(int)mux);
> +		extcon_set_property(info->edev, EXTCON_USB_HOST,
> +				    EXTCON_PROP_USB_SS,
> +				    (union extcon_property_value)(int)mux);
>  		extcon_set_property(info->edev, EXTCON_DISP_DP,
>  				    EXTCON_PROP_USB_SS,
>  				    (union extcon_property_value)(int)mux);
> @@ -237,6 +354,8 @@ static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info,
>  				    EXTCON_PROP_DISP_HPD,
>  				    (union extcon_property_value)(int)hpd);
>  
> +		extcon_sync(info->edev, EXTCON_USB);
> +		extcon_sync(info->edev, EXTCON_USB_HOST);
>  		extcon_sync(info->edev, EXTCON_DISP_DP);
>  
>  	} else if (hpd) {
> @@ -322,13 +441,28 @@ static int extcon_cros_ec_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> +	extcon_set_property_capability(info->edev, EXTCON_USB,
> +				       EXTCON_PROP_USB_VBUS);
> +	extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
> +				       EXTCON_PROP_USB_VBUS);
> +	extcon_set_property_capability(info->edev, EXTCON_USB,
> +				       EXTCON_PROP_USB_TYPEC_POLARITY);
> +	extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
> +				       EXTCON_PROP_USB_TYPEC_POLARITY);
>  	extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
>  				       EXTCON_PROP_USB_TYPEC_POLARITY);
> +	extcon_set_property_capability(info->edev, EXTCON_USB,
> +				       EXTCON_PROP_USB_SS);
> +	extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
> +				       EXTCON_PROP_USB_SS);
>  	extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
>  				       EXTCON_PROP_USB_SS);
>  	extcon_set_property_capability(info->edev, EXTCON_DISP_DP,
>  				       EXTCON_PROP_DISP_HPD);
>  
> +	info->dr = DR_NONE;
> +	info->pr = false;
> +
>  	platform_set_drvdata(pdev, info);
>  
>  	/* Get PD events from the EC */
> diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h
> index 2b16e95..c907353 100644
> --- a/include/linux/mfd/cros_ec_commands.h
> +++ b/include/linux/mfd/cros_ec_commands.h
> @@ -2904,16 +2904,33 @@ enum usb_pd_control_mux {
>  	USB_PD_CTRL_MUX_AUTO = 5,
>  };
>  
> +enum usb_pd_control_swap {
> +	USB_PD_CTRL_SWAP_NONE = 0,
> +	USB_PD_CTRL_SWAP_DATA = 1,
> +	USB_PD_CTRL_SWAP_POWER = 2,
> +	USB_PD_CTRL_SWAP_VCONN = 3,
> +	USB_PD_CTRL_SWAP_COUNT
> +};
> +
>  struct ec_params_usb_pd_control {
>  	uint8_t port;
>  	uint8_t role;
>  	uint8_t mux;
> +	uint8_t swap;
>  } __packed;
>  
>  #define PD_CTRL_RESP_ENABLED_COMMS      (1 << 0) /* Communication enabled */
>  #define PD_CTRL_RESP_ENABLED_CONNECTED  (1 << 1) /* Device connected */
>  #define PD_CTRL_RESP_ENABLED_PD_CAPABLE (1 << 2) /* Partner is PD capable */
>  
> +#define PD_CTRL_RESP_ROLE_POWER         (1 << 0) /* 0=SNK/1=SRC */
> +#define PD_CTRL_RESP_ROLE_DATA          (1 << 1) /* 0=UFP/1=DFP */
> +#define PD_CTRL_RESP_ROLE_VCONN         (1 << 2) /* Vconn status */
> +#define PD_CTRL_RESP_ROLE_DR_POWER      (1 << 3) /* Partner is dualrole power */
> +#define PD_CTRL_RESP_ROLE_DR_DATA       (1 << 4) /* Partner is dualrole data */
> +#define PD_CTRL_RESP_ROLE_USB_COMM      (1 << 5) /* Partner USB comm capable */
> +#define PD_CTRL_RESP_ROLE_EXT_POWERED   (1 << 6) /* Partner externally powerd */
> +
>  struct ec_response_usb_pd_control_v1 {
>  	uint8_t enabled;
>  	uint8_t role;
> 


-- 
Best Regards,
Chanwoo Choi
Samsung Electronics

^ permalink raw reply


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