Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 5/8] pinctrl: aspeed: Enable capture of off-SCU pinmux state
From: Joel Stanley @ 2016-09-29  6:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <a266046d34009e6e92c4c76699c550c2ba44bd5c.1474986045.git-series.andrew@aj.id.au>

On Wed, Sep 28, 2016 at 12:20 AM, Andrew Jeffery <andrew@aj.id.au> wrote:
> The System Control Unit IP in the Aspeed SoCs is typically where the
> pinmux configuration is found.
>
> But not always.
>
> On the AST2400 and AST2500 a number of pins depend on state in one of
> the SIO, LPC or GFX IP blocks, so add support to at least capture what
> that state is. The pinctrl engine for the Aspeed SoCs doesn't try to
> inspect or modify the state of the off-SCU IP blocks. Instead, it logs
> the state requirement with the expectation that the platform
> designer/maintainer arranges for the appropriate configuration to be
> applied through the associated drivers.

This is unfortunate.

This patch kicks the can down the road, but doesn't solve the problem
for a user who wants to configure some functionality that depends on
the non-SCU bits. Because of this I'm not sure if we want to put it in
the tree.

However, I'm not sure what a proper solution would look like. Perhaps
Linus can point out another SoC that has a similar problem?

Cheers,

Joel

>
> The IP block of interest is encoded in the reg member of struct
> aspeed_sig_desc. For compatibility with the existing code, the SCU is
> defined to have an IP value of 0.
>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
>  drivers/pinctrl/aspeed/pinctrl-aspeed.c | 53 +++++++++++++++++++++++---
>  drivers/pinctrl/aspeed/pinctrl-aspeed.h | 16 +++++++-
>  2 files changed, 61 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
> index 49aeba912531..21ef195d586f 100644
> --- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c
> +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
> @@ -14,6 +14,8 @@
>  #include "../core.h"
>  #include "pinctrl-aspeed.h"
>
> +const char *const aspeed_pinmux_ips[] = { "SCU", "SIO", "GFX", "LPC" };
> +
>  int aspeed_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
>  {
>         struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev);
> @@ -78,7 +80,9 @@ int aspeed_pinmux_get_fn_groups(struct pinctrl_dev *pctldev,
>  static inline void aspeed_sig_desc_print_val(
>                 const struct aspeed_sig_desc *desc, bool enable, u32 rv)
>  {
> -       pr_debug("SCU%x[0x%08x]=0x%x, got 0x%x from 0x%08x\n", desc->reg,
> +       pr_debug("Want %s%lX[0x%08X]=0x%X, got 0x%X from 0x%08X\n",
> +                       aspeed_pinmux_ips[SIG_DESC_IP_FROM_REG(desc->reg)],
> +                       SIG_DESC_OFFSET_FROM_REG(desc->reg),
>                         desc->mask, enable ? desc->enable : desc->disable,
>                         (rv & desc->mask) >> __ffs(desc->mask), rv);
>  }
> @@ -105,6 +109,8 @@ static bool aspeed_sig_desc_eval(const struct aspeed_sig_desc *desc,
>         unsigned int raw;
>         u32 want;
>
> +       WARN_ON(SIG_DESC_IP_FROM_REG(desc->reg) != ASPEED_IP_SCU);
> +
>         if (regmap_read(map, desc->reg, &raw) < 0)
>                 return false;
>
> @@ -142,9 +148,19 @@ static bool aspeed_sig_expr_eval(const struct aspeed_sig_expr *expr,
>
>         for (i = 0; i < expr->ndescs; i++) {
>                 const struct aspeed_sig_desc *desc = &expr->descs[i];
> +               size_t ip = SIG_DESC_IP_FROM_REG(desc->reg);
> +
> +               if (ip == ASPEED_IP_SCU) {
> +                       if (!aspeed_sig_desc_eval(desc, enabled, map))
> +                               return false;
> +               } else {
> +                       size_t offset = SIG_DESC_OFFSET_FROM_REG(desc->reg);
> +                       const char *ip_name = aspeed_pinmux_ips[ip];
> +
> +                       pr_debug("Ignoring configuration of field %s%X[0x%08X]\n",
> +                                ip_name, offset, desc->mask);
> +               }
>
> -               if (!aspeed_sig_desc_eval(desc, enabled, map))
> -                       return false;
>         }
>
>         return true;
> @@ -170,7 +186,14 @@ static bool aspeed_sig_expr_set(const struct aspeed_sig_expr *expr,
>         for (i = 0; i < expr->ndescs; i++) {
>                 bool ret;
>                 const struct aspeed_sig_desc *desc = &expr->descs[i];
> +
> +               size_t offset = SIG_DESC_OFFSET_FROM_REG(desc->reg);
> +               size_t ip = SIG_DESC_IP_FROM_REG(desc->reg);
> +               bool is_scu = (ip == ASPEED_IP_SCU);
> +               const char *ip_name = aspeed_pinmux_ips[ip];
> +
>                 u32 pattern = enable ? desc->enable : desc->disable;
> +               u32 val = (pattern << __ffs(desc->mask));
>
>                 /*
>                  * Strap registers are configured in hardware or by early-boot
> @@ -179,11 +202,27 @@ static bool aspeed_sig_expr_set(const struct aspeed_sig_expr *expr,
>                  * deconfigured and is the reason we re-evaluate after writing
>                  * all descriptor bits.
>                  */
> -               if (desc->reg == HW_STRAP1 || desc->reg == HW_STRAP2)
> +               if (is_scu && (offset == HW_STRAP1 || offset == HW_STRAP2))
>                         continue;
>
> -               ret = regmap_update_bits(map, desc->reg, desc->mask,
> -                               pattern << __ffs(desc->mask)) == 0;
> +               /*
> +                * Sometimes we need help from IP outside the SCU to activate a
> +                * mux request. Report that we need its cooperation.
> +                */
> +               if (enable && !is_scu) {
> +                       pr_debug("Pinmux request for %s requires cooperation of %s IP: Need (%s%X[0x%08X] = 0x%08X\n",
> +                               expr->function, ip_name, ip_name, offset,
> +                               desc->mask, val);
> +               }
> +
> +               /* And only read/write SCU registers */
> +               if (!is_scu) {
> +                       pr_debug("Skipping configuration of field %s%X[0x%08X]\n",
> +                                       ip_name, offset, desc->mask);
> +                       continue;
> +               }
> +
> +               ret = regmap_update_bits(map, desc->reg, desc->mask, val) == 0;
>
>                 if (!ret)
>                         return ret;
> @@ -343,6 +382,8 @@ int aspeed_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
>                 const struct aspeed_sig_expr **funcs;
>                 const struct aspeed_sig_expr ***prios;
>
> +               pr_debug("Muxing pin %d for %s\n", pin, pfunc->name);
> +
>                 if (!pdesc)
>                         return -EINVAL;
>
> diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.h b/drivers/pinctrl/aspeed/pinctrl-aspeed.h
> index 3e72ef8c54bf..4384407d77fb 100644
> --- a/drivers/pinctrl/aspeed/pinctrl-aspeed.h
> +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.h
> @@ -232,6 +232,15 @@
>   * group.
>   */
>
> +#define ASPEED_IP_SCU  0
> +#define ASPEED_IP_SIO  1
> +#define ASPEED_IP_GFX  2
> +#define ASPEED_IP_LPC  3
> +
> +#define SIG_DESC_TO_REG(ip, offset)    (((ip) << 24) | (offset))
> +#define SIG_DESC_IP_FROM_REG(reg)      (((reg) >> 24) & GENMASK(7, 0))
> +#define SIG_DESC_OFFSET_FROM_REG(reg)  ((reg) & GENMASK(11, 0))
> +
>  /*
>   * The "Multi-function Pins Mapping and Control" table in the SoC datasheet
>   * references registers by the device/offset mnemonic. The register macros
> @@ -261,7 +270,10 @@
>    * A signal descriptor, which describes the register, bits and the
>    * enable/disable values that should be compared or written.
>    *
> -  * @reg: The register offset from base in bytes
> +  * @reg: Split into three fields:
> +  *       31:24: IP selector
> +  *       23:12: Reserved
> +  *       11:0: Register offset
>    * @mask: The mask to apply to the register. The lowest set bit of the mask is
>    *        used to derive the shift value.
>    * @enable: The value that enables the function. Value should be in the LSBs,
> @@ -270,7 +282,7 @@
>    *           LSBs, not at the position of the mask.
>    */
>  struct aspeed_sig_desc {
> -       unsigned int reg;
> +       u32 reg;
>         u32 mask;
>         u32 enable;
>         u32 disable;
> --
> git-series 0.8.10

^ permalink raw reply

* [PATCH RFC 6/8] ARM: dts: r8a7743: add SYS-DMAC support
From: Geert Uytterhoeven @ 2016-09-29  6:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <be7575a5-e9a0-05a6-5b2d-803632c7edac@cogentembedded.com>

Hi Sergei,

On Wed, Sep 28, 2016 at 10:09 PM, Sergei Shtylyov
<sergei.shtylyov@cogentembedded.com> wrote:
> On 09/19/2016 11:19 AM, Geert Uytterhoeven wrote:
>>> --- renesas.orig/arch/arm/boot/dts/r8a7743.dtsi
>>> +++ renesas/arch/arm/boot/dts/r8a7743.dtsi
>>> @@ -90,6 +90,70 @@
>>>                         #power-domain-cells = <1>;
>>>                 };
>>>
>>> +               dmac0: dma-controller at e6700000 {
>>> +                       compatible = "renesas,dmac-r8a7743",
>>
>>
>> To be documented.
>
>    The R-Car DMAC binding has "renesas,dmac-<soctype>" documented.
> I can add the mention of RZ/G and new examples though...

Just adding a line for RZ/G1M should be sufficient. Thanks!

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* [PATCH] MAINTAINERS: update entry for atmel_serial driver
From: Greg Kroah-Hartman @ 2016-09-29  6:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160928154735.25265-1-nicolas.ferre@atmel.com>

On Wed, Sep 28, 2016 at 05:47:35PM +0200, Nicolas Ferre wrote:
> Change maintainer for the serial driver found on most of the
> Microchip / Atmel MPUs and take advantage of the move to rename
> and reorder the entry.
> I'm happy that Richard is taking over the maintenance of this
> driver.
> 
> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
> Acked-by: Richard Genoud <richard.genoud@gmail.com>
> ---
>  MAINTAINERS | 11 ++++++-----
>  1 file changed, 6 insertions(+), 5 deletions(-)

This doesn't apply to my tty-next tree at all, what did you make it
against?

thanks,

greg k-h

^ permalink raw reply

* [PATCH] MAINTAINERS: update entry for atmel_serial driver
From: Richard Genoud @ 2016-09-29  6:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475078653.2685.1.camel@perches.com>

2016-09-28 18:04 GMT+02:00 Joe Perches <joe@perches.com>:
> On Wed, 2016-09-28 at 17:47 +0200, Nicolas Ferre wrote:
>> Change maintainer for the serial driver found on most of the
>> Microchip / Atmel MPUs and take advantage of the move to rename
>> and reorder the entry.
> []
>> diff --git a/MAINTAINERS b/MAINTAINERS
> []
>> +MICROCHIP / ATMEL AT91 / AT32 SERIAL DRIVER
>> +M:   Richard Genoud <richard.genoud@gmail.com>
>> +S:   Maintained
>> +F:   drivers/tty/serial/atmel_serial.c
>> +F:   include/linux/atmel_serial.h
>
> Thanks Richard.
>
> include/linux is pretty cluttered with random files.
>
> atmel_serial.h is #include exactly once.
>
> Please move it out of include/linux and into
> drivers/tty/serial/ one day.
No problem, I'll add that to my TBD list !

Richard.

^ permalink raw reply

* [PATCH 0/3] [v2] Add basic ACPI support to the Qualcomm Technologies EMAC driver
From: David Miller @ 2016-09-29  5:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475081924-12362-1-git-send-email-timur@codeaurora.org>

From: Timur Tabi <timur@codeaurora.org>
Date: Wed, 28 Sep 2016 11:58:41 -0500

> This patch series adds support to the EMAC driver for extracting addresses,
> interrupts, and some _DSDs (properties) from ACPI.  The first two patches
> clean up the code, and the third patch adds ACPI-specific functionality.
> 
> The first patch fixes a bug with handling the platform_device for the
> internal PHY.  This phy is treated as a separate device in both DT and
> ACPI, but since the platform is not released automatically when the 
> driver unloads, managed functions like devm_ioremap_resource cannot be
> used. 
> 
> The second patch replaces of_get_mac_address with its platform-independent
> equivalent device_get_mac_address.  
> 
> The third patch parses the ACPI tables to obtain the platform_device for
> the primary EMAC node ("QCOM8070") and the internal phy node ("QCOM8071").

Series applied, thanks.

^ permalink raw reply

* [PATCH 2/2] drm/mediatek: clear IRQ status before enable OVL interrupt
From: CK Hu @ 2016-09-29  5:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475119789-64619-3-git-send-email-bibby.hsieh@mediatek.com>

Acked-by: CK Hu <ck.hu@mediatek.com>

On Thu, 2016-09-29 at 11:29 +0800, Bibby Hsieh wrote:
> To make sure that the first vblank IRQ after enabling
> vblank isn't too short or immediate, we have to clear
> the IRQ status before enable OVL interrupt.
> 
> Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_disp_ovl.c |    1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> index 019b7ca..f75c5b5 100644
> --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> @@ -80,6 +80,7 @@ static void mtk_ovl_enable_vblank(struct mtk_ddp_comp *comp,
>  						 ddp_comp);
>  
>  	priv->crtc = crtc;
> +	writel(0x0, comp->regs + DISP_REG_OVL_INTSTA);
>  	writel_relaxed(OVL_FME_CPL_INT, comp->regs + DISP_REG_OVL_INTEN);
>  }
>  

^ permalink raw reply

* [PATCH v3 2/7] i2c: bcm2835: Protect against unexpected TXW/RXR interrupts
From: Stefan Wahren @ 2016-09-29  5:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <d93a0590-1783-b913-9362-007628cb17d6@tronnes.org>


> Noralf Tr?nnes <noralf@tronnes.org> hat am 29. September 2016 um 00:22
> geschrieben:
> 
> 
> 
> Den 29.09.2016 00:00, skrev Eric Anholt:
> > Noralf Tr?nnes <noralf@tronnes.org> writes:
> >
> >> If an unexpected TXW or RXR interrupt occurs (msg_buf_remaining == 0),
> >> the driver has no way to fill/drain the FIFO to stop the interrupts.
> >> In this case the controller has to be disabled and the transfer
> >> completed to avoid hang.
> >>
> >> (CLKT | ERR) and DONE interrupts are completed in their own paths, and
> >> the controller is disabled in the transfer function after completion.
> >> Unite the code paths and do disabling inside the interrupt routine.
> >>
> >> Clear interrupt status bits in the united completion path instead of
> >> trying to do it on every interrupt which isn't necessary.
> >> Only CLKT, ERR and DONE can be cleared that way.
> >>
> >> Add the status value to the error value in case of TXW/RXR errors to
> >> distinguish them from the other S_LEN error.
> > I was surprised that not writing the TXW/RXR bits on handling their
> > interrupts was OK, given that we were doing so before, but it's a level
> > interrupt and those bits are basically ignored on write.
> >
> > This patch and 3, 4, and 6 are:
> >
> > Reviewed-by: Eric Anholt <eric@anholt.net>
> >
> > Patch 5 is:
> >
> > Acked-by: Eric Anholt <eric@anholt.net>
> >
> > Note for future debug: The I2C_C_CLEAR on errors will take some time to
> > resolve -- if you were in non-idle state and I2C_C_READ, it sets an
> > abort_rx flag and runs through the state machine to send a NACK and a
> > STOP, I think.  Since we're setting CLEAR without I2CEN, that NACK will
> > be hanging around queued up for next time we start the engine.
> 
> Maybe you're able to explain the issues I had with reset:
> https://github.com/raspberrypi/linux/issues/1653
> 
> Should we put your note into the commit message?
> It will most likely be lost if it just stays in this email.

I prefer to have this kind of information as a code comment.

> 
> Noralf.
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH 1/2] drm/mediatek: set vblank_disable_allowed to true
From: CK Hu @ 2016-09-29  5:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475119789-64619-2-git-send-email-bibby.hsieh@mediatek.com>

Acked-by: CK Hu <ck.hu@mediatek.com>

On Thu, 2016-09-29 at 11:29 +0800, Bibby Hsieh wrote:
> MTK DRM driver didn't set the vblank_disable_allowed to
> true, it cause that the irq_handler is called every
> 16.6 ms (every vblank) when the display didn't be updated.
> 
> Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_drm_drv.c |    1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> index eebb7d8..941ec5f 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> @@ -200,6 +200,7 @@ static int mtk_drm_kms_init(struct drm_device *drm)
>  	if (ret < 0)
>  		goto err_component_unbind;
>  
> +	drm->vblank_disable_allowed = true;
>  	drm_kms_helper_poll_init(drm);
>  	drm_mode_config_reset(drm);
>  

^ permalink raw reply

* [PATCH v19 05/12] fpga-mgr: add fpga image information struct
From: Alan Tull @ 2016-09-29  4:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAAtXAHcdSsVUE4hQ6isCp7_35ZKb60EAKAgtGwBR41apwPpe=g@mail.gmail.com>

On Wed, Sep 28, 2016 at 6:41 PM, Moritz Fischer
<moritz.fischer@ettus.com> wrote:
Hi Moritz,

> Hi Alan,
>
> generally ok with the change.

Cool!

>
> On Wed, Sep 28, 2016 at 11:21 AM, Alan Tull <atull@opensource.altera.com> wrote:
>
>> -int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, const char *buf,
>> -                     size_t count)
>> +int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info,
>> +                     const char *buf, size_t count)
>
> Doesn't this break the both socfpga and zynq if [6/12] and [7/12] are
> not part of this commit?
> i.e shouldn't 5,6 and 7 be a single commit?

Yeah, squashing those would improve bisectability.

Alan
aka atull

>
> Cheers,
> Moritz

^ permalink raw reply

* [PATCH] drm/mediatek: fix a typo
From: CK Hu @ 2016-09-29  4:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475119322-58657-1-git-send-email-bibby.hsieh@mediatek.com>

Acked-by: CK Hu <ck.hu@mediatek.com>

On Thu, 2016-09-29 at 11:22 +0800, Bibby Hsieh wrote:
> Fix the typo: OD_RELAYMODE->OD_CFG
> 
> Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> index df33b3c..aa5f20f 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> @@ -123,7 +123,7 @@ static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
>  			  unsigned int bpc)
>  {
>  	writel(w << 16 | h, comp->regs + DISP_OD_SIZE);
> -	writel(OD_RELAYMODE, comp->regs + OD_RELAYMODE);
> +	writel(OD_RELAYMODE, comp->regs + OD_CFG);
>  	mtk_dither_set(comp, bpc, DISP_OD_CFG);
>  }
>  

^ permalink raw reply

* [PATCH 2/2] drm/mediatek: clear IRQ status before enable OVL interrupt
From: Bibby Hsieh @ 2016-09-29  3:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475119789-64619-1-git-send-email-bibby.hsieh@mediatek.com>

To make sure that the first vblank IRQ after enabling
vblank isn't too short or immediate, we have to clear
the IRQ status before enable OVL interrupt.

Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c |    1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
index 019b7ca..f75c5b5 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -80,6 +80,7 @@ static void mtk_ovl_enable_vblank(struct mtk_ddp_comp *comp,
 						 ddp_comp);
 
 	priv->crtc = crtc;
+	writel(0x0, comp->regs + DISP_REG_OVL_INTSTA);
 	writel_relaxed(OVL_FME_CPL_INT, comp->regs + DISP_REG_OVL_INTEN);
 }
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 1/2] drm/mediatek: set vblank_disable_allowed to true
From: Bibby Hsieh @ 2016-09-29  3:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475119789-64619-1-git-send-email-bibby.hsieh@mediatek.com>

MTK DRM driver didn't set the vblank_disable_allowed to
true, it cause that the irq_handler is called every
16.6 ms (every vblank) when the display didn't be updated.

Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_drm_drv.c |    1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index eebb7d8..941ec5f 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -200,6 +200,7 @@ static int mtk_drm_kms_init(struct drm_device *drm)
 	if (ret < 0)
 		goto err_component_unbind;
 
+	drm->vblank_disable_allowed = true;
 	drm_kms_helper_poll_init(drm);
 	drm_mode_config_reset(drm);
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 0/2] fix issue: vblank interrupts are never disabled
From: Bibby Hsieh @ 2016-09-29  3:29 UTC (permalink / raw)
  To: linux-arm-kernel

Clean the interrupt status before enable interrupt
and set the vblank_disable_allowed to fix the issue.

Bibby Hsieh (2):
  drm/mediatek: set vblank_disable_allowed to true
  drm/mediatek: clear IRQ status before enable OVL interrupt

 drivers/gpu/drm/mediatek/mtk_disp_ovl.c |    1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
 2 files changed, 2 insertions(+)

-- 
1.7.9.5

^ permalink raw reply

* [PATCH] drm/mediatek: fix a typo
From: Bibby Hsieh @ 2016-09-29  3:22 UTC (permalink / raw)
  To: linux-arm-kernel

Fix the typo: OD_RELAYMODE->OD_CFG

Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index df33b3c..aa5f20f 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -123,7 +123,7 @@ static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
 			  unsigned int bpc)
 {
 	writel(w << 16 | h, comp->regs + DISP_OD_SIZE);
-	writel(OD_RELAYMODE, comp->regs + OD_RELAYMODE);
+	writel(OD_RELAYMODE, comp->regs + OD_CFG);
 	mtk_dither_set(comp, bpc, DISP_OD_CFG);
 }
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v5 3/3] drm/mediatek: modify the factor to make the pll_rate set in the 1G-2G range
From: Bibby Hsieh @ 2016-09-29  3:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475118135-56780-1-git-send-email-bibby.hsieh@mediatek.com>

From: Junzhi Zhao <junzhi.zhao@mediatek.com>

Currently, the code sets the "pll" to the desired multiple
of the pixel clock manully(4*3m 8*3,etc).  The valid range
of the pll is 1G-2G, however, when the pixel clock is bigger
than 167MHz,  the "pll" will be set to a invalid value( > 2G),
then the "pll" will be 2GHz, thus the pixel clock will be in
correct. Change the factor to make the "pll" be set in the
(1G, 2G) range.

Signed-off-by: Junzhi Zhao <junzhi.zhao@mediatek.com>
Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_dpi.c |    9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index 0186e50..90fb831 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -432,11 +432,16 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 	unsigned long pll_rate;
 	unsigned int factor;
 
+	/* let pll_rate can fix the valid range of tvdpll (1G~2GHz) */
 	pix_rate = 1000UL * mode->clock;
-	if (mode->clock <= 74000)
+	if (mode->clock <= 27000)
+		factor = 16 * 3;
+	else if (mode->clock <= 84000)
 		factor = 8 * 3;
-	else
+	else if (mode->clock <= 167000)
 		factor = 4 * 3;
+	else
+		factor = 2 * 3;
 	pll_rate = pix_rate * factor;
 
 	dev_dbg(dpi->dev, "Want PLL %lu Hz, pixel clock %lu Hz\n",
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v5 2/3] drm/mediatek: enhance the HDMI driving current
From: Bibby Hsieh @ 2016-09-29  3:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475118135-56780-1-git-send-email-bibby.hsieh@mediatek.com>

From: Junzhi Zhao <junzhi.zhao@mediatek.com>

In order to improve 4K resolution performance,
we have to enhance the HDMI driving current
when clock rate is greater than 165MHz.

Signed-off-by: Junzhi Zhao <junzhi.zhao@mediatek.com>
Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c |   42 +++++++++++++++++-------
 1 file changed, 30 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c
index 8a24754..51cb9cf 100644
--- a/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c
+++ b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c
@@ -265,6 +265,9 @@ static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 	struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
 	unsigned int pre_div;
 	unsigned int div;
+	unsigned int pre_ibias;
+	unsigned int hdmi_ibias;
+	unsigned int imp_en;
 
 	dev_dbg(hdmi_phy->dev, "%s: %lu Hz, parent: %lu Hz\n", __func__,
 		rate, parent_rate);
@@ -298,18 +301,31 @@ static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 			  (0x1 << PLL_BR_SHIFT),
 			  RG_HDMITX_PLL_BP | RG_HDMITX_PLL_BC |
 			  RG_HDMITX_PLL_BR);
-	mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON3, RG_HDMITX_PRD_IMP_EN);
+	if (rate < 165000000) {
+		mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON3,
+					RG_HDMITX_PRD_IMP_EN);
+		pre_ibias = 0x3;
+		imp_en = 0x0;
+		hdmi_ibias = hdmi_phy->ibias;
+	} else {
+		mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON3,
+				      RG_HDMITX_PRD_IMP_EN);
+		pre_ibias = 0x6;
+		imp_en = 0xf;
+		hdmi_ibias = hdmi_phy->ibias_up;
+	}
 	mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON4,
-			  (0x3 << PRD_IBIAS_CLK_SHIFT) |
-			  (0x3 << PRD_IBIAS_D2_SHIFT) |
-			  (0x3 << PRD_IBIAS_D1_SHIFT) |
-			  (0x3 << PRD_IBIAS_D0_SHIFT),
+			  (pre_ibias << PRD_IBIAS_CLK_SHIFT) |
+			  (pre_ibias << PRD_IBIAS_D2_SHIFT) |
+			  (pre_ibias << PRD_IBIAS_D1_SHIFT) |
+			  (pre_ibias << PRD_IBIAS_D0_SHIFT),
 			  RG_HDMITX_PRD_IBIAS_CLK |
 			  RG_HDMITX_PRD_IBIAS_D2 |
 			  RG_HDMITX_PRD_IBIAS_D1 |
 			  RG_HDMITX_PRD_IBIAS_D0);
 	mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3,
-			  (0x0 << DRV_IMP_EN_SHIFT), RG_HDMITX_DRV_IMP_EN);
+			  (imp_en << DRV_IMP_EN_SHIFT),
+			  RG_HDMITX_DRV_IMP_EN);
 	mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6,
 			  (hdmi_phy->drv_imp_clk << DRV_IMP_CLK_SHIFT) |
 			  (hdmi_phy->drv_imp_d2 << DRV_IMP_D2_SHIFT) |
@@ -318,12 +334,14 @@ static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 			  RG_HDMITX_DRV_IMP_CLK | RG_HDMITX_DRV_IMP_D2 |
 			  RG_HDMITX_DRV_IMP_D1 | RG_HDMITX_DRV_IMP_D0);
 	mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON5,
-			  (hdmi_phy->ibias << DRV_IBIAS_CLK_SHIFT) |
-			  (hdmi_phy->ibias << DRV_IBIAS_D2_SHIFT) |
-			  (hdmi_phy->ibias << DRV_IBIAS_D1_SHIFT) |
-			  (hdmi_phy->ibias << DRV_IBIAS_D0_SHIFT),
-			  RG_HDMITX_DRV_IBIAS_CLK | RG_HDMITX_DRV_IBIAS_D2 |
-			  RG_HDMITX_DRV_IBIAS_D1 | RG_HDMITX_DRV_IBIAS_D0);
+			  (hdmi_ibias << DRV_IBIAS_CLK_SHIFT) |
+			  (hdmi_ibias << DRV_IBIAS_D2_SHIFT) |
+			  (hdmi_ibias << DRV_IBIAS_D1_SHIFT) |
+			  (hdmi_ibias << DRV_IBIAS_D0_SHIFT),
+			  RG_HDMITX_DRV_IBIAS_CLK |
+			  RG_HDMITX_DRV_IBIAS_D2 |
+			  RG_HDMITX_DRV_IBIAS_D1 |
+			  RG_HDMITX_DRV_IBIAS_D0);
 	return 0;
 }
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v5 1/3] drm/mediatek: do mtk_hdmi_send_infoframe after HDMI clock enable
From: Bibby Hsieh @ 2016-09-29  3:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475118135-56780-1-git-send-email-bibby.hsieh@mediatek.com>

From: Junzhi Zhao <junzhi.zhao@mediatek.com>

The mtk_hdmi_send_infoframe have to
be run after PLL and PIXEL clock of HDMI enable.
Make sure that HDMI inforframes can be sent
successfully.

Signed-off-by: Junzhi Zhao <junzhi.zhao@mediatek.com>
Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_hdmi.c |   17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index 334562d..875b045 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -1133,12 +1133,6 @@ static int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi,
 	phy_power_on(hdmi->phy);
 	mtk_hdmi_aud_output_config(hdmi, mode);
 
-	mtk_hdmi_setup_audio_infoframe(hdmi);
-	mtk_hdmi_setup_avi_infoframe(hdmi, mode);
-	mtk_hdmi_setup_spd_infoframe(hdmi, "mediatek", "On-chip HDMI");
-	if (mode->flags & DRM_MODE_FLAG_3D_MASK)
-		mtk_hdmi_setup_vendor_specific_infoframe(hdmi, mode);
-
 	mtk_hdmi_hw_vid_black(hdmi, false);
 	mtk_hdmi_hw_aud_unmute(hdmi);
 	mtk_hdmi_hw_send_av_unmute(hdmi);
@@ -1401,6 +1395,16 @@ static void mtk_hdmi_bridge_pre_enable(struct drm_bridge *bridge)
 	hdmi->powered = true;
 }
 
+static void mtk_hdmi_send_infoframe(struct mtk_hdmi *hdmi,
+				    struct drm_display_mode *mode)
+{
+	mtk_hdmi_setup_audio_infoframe(hdmi);
+	mtk_hdmi_setup_avi_infoframe(hdmi, mode);
+	mtk_hdmi_setup_spd_infoframe(hdmi, "mediatek", "On-chip HDMI");
+	if (mode->flags & DRM_MODE_FLAG_3D_MASK)
+		mtk_hdmi_setup_vendor_specific_infoframe(hdmi, mode);
+}
+
 static void mtk_hdmi_bridge_enable(struct drm_bridge *bridge)
 {
 	struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
@@ -1409,6 +1413,7 @@ static void mtk_hdmi_bridge_enable(struct drm_bridge *bridge)
 	clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]);
 	clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]);
 	phy_power_on(hdmi->phy);
+	mtk_hdmi_send_infoframe(hdmi, &hdmi->mode);
 
 	hdmi->enabled = true;
 }
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v5 0/3] MT8173 HDMI 4K support
From: Bibby Hsieh @ 2016-09-29  3:02 UTC (permalink / raw)
  To: linux-arm-kernel

This is MT8173 HDMI 4K support PATCH v5, based on 4.8-rc1.

In order to support HDMI 4K on MT8173,
we have to make some modifications.
1) Make sure that mtk_hdmi_send_infoframe is sent successfully.
2) Enhance the HDMI driving current to improve performance.
3) Make sure that pixel clock is 297MHz when resolution is 4K.

Changes since v4:
 - Update commit message and patch title.

Changes since v3:
 - Rebase to 4.8-rc1.
 - The valid range of tvdpll is 1G to 2G Hz, so, we Change the
   if statement of mode->clock to fit that and add a comment.

Changes since v2:
 - Remove the change about preparation for MT2701 support.

Changes since v1:
 - According to the suggestion from philipp, We use the new
   dpi0_sel rate set method.
 - calls clk_set_rate to set the dpi0_sel according to the
   pixel clock.
 - Remove the direct access to all the intermediate clock part.
 - Remove the intermediate tvdpll_d* clocks in dts.
 - According to suggestion from CK, we rename the clock parse
   function and remove it from mtk_dpi_conf struct.
 - Merges the hdmi Pll set rate for pixel clock greater than
   165MHz and smaller parts.

The PATCH depends on the following patch:
https://patchwork.kernel.org/patch/9262575/
(arm64: dts: mt8173: add mmsel clocks for 4K support)

Junzhi Zhao (3):
  drm/mediatek: do mtk_hdmi_send_infoframe after HDMI clock enable
  drm/mediatek: enhance the HDMI driving current
  drm/mediatek: modify the factor to make the pll_rate set in the 1G-2G
    range

 drivers/gpu/drm/mediatek/mtk_dpi.c             |    9 +++--
 drivers/gpu/drm/mediatek/mtk_hdmi.c            |   17 ++++++----
 drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c |   42 +++++++++++++++++-------
 3 files changed, 48 insertions(+), 20 deletions(-)

-- 
1.7.9.5

^ permalink raw reply

* [PATCH] clk: bcm2835: Clamp the PLL's requested rate to the hardware limits.
From: Eric Anholt @ 2016-09-29  2:23 UTC (permalink / raw)
  To: linux-arm-kernel

Fixes setting low-resolution video modes on HDMI.  Now the PLLH_PIX
divider adjusts itself until the PLLH is within bounds.

Signed-off-by: Eric Anholt <eric@anholt.net>
---
 drivers/clk/bcm/clk-bcm2835.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 7a7970865c2d..fedc88908e61 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -499,8 +499,13 @@ static long bcm2835_pll_rate_from_divisors(unsigned long parent_rate,
 static long bcm2835_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 				   unsigned long *parent_rate)
 {
+	struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw);
+	const struct bcm2835_pll_data *data = pll->data;
 	u32 ndiv, fdiv;
 
+	rate = max(data->min_rate, rate);
+	rate = min(data->max_rate, rate);
+
 	bcm2835_pll_choose_ndiv_and_fdiv(rate, *parent_rate, &ndiv, &fdiv);
 
 	return bcm2835_pll_rate_from_divisors(*parent_rate, ndiv, fdiv, 1);
@@ -605,13 +610,6 @@ static int bcm2835_pll_set_rate(struct clk_hw *hw,
 	u32 ana[4];
 	int i;
 
-	if (rate < data->min_rate || rate > data->max_rate) {
-		dev_err(cprman->dev, "%s: rate out of spec: %lu vs (%lu, %lu)\n",
-			clk_hw_get_name(hw), rate,
-			data->min_rate, data->max_rate);
-		return -EINVAL;
-	}
-
 	if (rate > data->max_fb_rate) {
 		use_fb_prediv = true;
 		rate /= 2;
-- 
2.9.3

^ permalink raw reply related

* [PATCH V4 10/10] dmaengine: qcom_hidma: add MSI support for interrupts
From: Sinan Kaya @ 2016-09-29  2:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475115167-5898-1-git-send-email-okaya@codeaurora.org>

The interrupts can now be delivered as platform MSI interrupts
on newer platforms. The code looks for a new OF and ACPI strings
in order to enable the functionality.

Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
---
 drivers/dma/qcom/hidma.c    | 129 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/dma/qcom/hidma.h    |   2 +
 drivers/dma/qcom/hidma_ll.c |   8 +++
 3 files changed, 134 insertions(+), 5 deletions(-)

diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c
index e244e10..f4fe4ee 100644
--- a/drivers/dma/qcom/hidma.c
+++ b/drivers/dma/qcom/hidma.c
@@ -56,6 +56,7 @@
 #include <linux/irq.h>
 #include <linux/atomic.h>
 #include <linux/pm_runtime.h>
+#include <linux/msi.h>
 
 #include "../dmaengine.h"
 #include "hidma.h"
@@ -70,6 +71,7 @@
 #define HIDMA_ERR_INFO_SW			0xFF
 #define HIDMA_ERR_CODE_UNEXPECTED_TERMINATE	0x0
 #define HIDMA_NR_DEFAULT_DESC			10
+#define HIDMA_MSI_INTS				11
 
 static inline struct hidma_dev *to_hidma_dev(struct dma_device *dmadev)
 {
@@ -553,6 +555,15 @@ static irqreturn_t hidma_chirq_handler(int chirq, void *arg)
 	return hidma_ll_inthandler(chirq, lldev);
 }
 
+static irqreturn_t hidma_chirq_handler_msi(int chirq, void *arg)
+{
+	struct hidma_lldev **lldevp = arg;
+	struct hidma_dev *dmadev = to_hidma_dev_from_lldev(lldevp);
+
+	return hidma_ll_inthandler_msi(chirq, *lldevp,
+				       1 << (chirq - dmadev->msi_virqbase));
+}
+
 static ssize_t hidma_show_values(struct device *dev,
 				 struct device_attribute *attr, char *buf)
 {
@@ -590,6 +601,99 @@ static int hidma_create_sysfs_entry(struct hidma_dev *dev, char *name,
 	return device_create_file(dev->ddev.dev, attrs);
 }
 
+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
+static void hidma_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
+{
+	struct device *dev = msi_desc_to_dev(desc);
+	struct hidma_dev *dmadev = dev_get_drvdata(dev);
+
+	if (!desc->platform.msi_index) {
+		writel(msg->address_lo, dmadev->dev_evca + 0x118);
+		writel(msg->address_hi, dmadev->dev_evca + 0x11C);
+		writel(msg->data, dmadev->dev_evca + 0x120);
+	}
+}
+
+static void hidma_free_msis(void *data)
+{
+	struct device *dev = data;
+
+	platform_msi_domain_free_irqs(dev);
+}
+#endif
+
+static int hidma_request_msi(struct hidma_dev *dmadev,
+			     struct platform_device *pdev)
+{
+#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
+	int rc;
+	struct msi_desc *desc;
+	struct msi_desc *failed_desc = NULL;
+
+	rc = platform_msi_domain_alloc_irqs(&pdev->dev, HIDMA_MSI_INTS,
+					    hidma_write_msi_msg);
+	if (rc)
+		return rc;
+
+	for_each_msi_entry(desc, &pdev->dev) {
+		if (!desc->platform.msi_index)
+			dmadev->msi_virqbase = desc->irq;
+
+		rc = devm_request_irq(&pdev->dev, desc->irq,
+				       hidma_chirq_handler_msi,
+				       0, "qcom-hidma-msi",
+				       &dmadev->lldev);
+		if (rc) {
+			failed_desc = desc;
+			break;
+		}
+	}
+
+	if (rc) {
+		/* free allocated MSI interrupts above */
+		for_each_msi_entry(desc, &pdev->dev) {
+			if (desc == failed_desc)
+				break;
+			devm_free_irq(&pdev->dev, desc->irq,
+				      &dmadev->lldev);
+		}
+	} else {
+		/* Add callback to free MSIs on teardown */
+		devm_add_action(&pdev->dev, hidma_free_msis,
+				&pdev->dev);
+		hidma_ll_setup_irq(dmadev->lldev, true);
+
+	}
+	if (rc)
+		dev_warn(&pdev->dev,
+			 "failed to request MSI irq, falling back to wired IRQ\n");
+	return rc;
+#else
+	return -EINVAL;
+#endif
+}
+
+static bool hidma_msi_capable(struct device *dev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	const char *of_compat;
+	int ret = -EINVAL;
+
+	if (!adev || acpi_disabled) {
+		ret = device_property_read_string(dev, "compatible",
+						  &of_compat);
+		if (ret)
+			return false;
+
+		ret = strcmp(of_compat, "qcom,hidma-1.1");
+	} else {
+#ifdef CONFIG_ACPI
+		ret = strcmp(acpi_device_hid(adev), "QCOM8062");
+#endif
+	}
+	return ret == 0;
+}
+
 static int hidma_probe(struct platform_device *pdev)
 {
 	struct hidma_dev *dmadev;
@@ -599,6 +703,7 @@ static int hidma_probe(struct platform_device *pdev)
 	void __iomem *evca;
 	void __iomem *trca;
 	int rc;
+	bool msi;
 
 	pm_runtime_set_autosuspend_delay(&pdev->dev, HIDMA_AUTOSUSPEND_TIMEOUT);
 	pm_runtime_use_autosuspend(&pdev->dev);
@@ -660,6 +765,12 @@ static int hidma_probe(struct platform_device *pdev)
 	dmadev->ddev.device_terminate_all = hidma_terminate_all;
 	dmadev->ddev.copy_align = 8;
 
+	/*
+	 * Determine the MSI capability of the platform. Old HW doesn't
+	 * support MSI.
+	 */
+	msi = hidma_msi_capable(&pdev->dev);
+
 	device_property_read_u32(&pdev->dev, "desc-count",
 				 &dmadev->nr_descriptors);
 
@@ -688,10 +799,17 @@ static int hidma_probe(struct platform_device *pdev)
 		goto dmafree;
 	}
 
-	rc = devm_request_irq(&pdev->dev, chirq, hidma_chirq_handler, 0,
-			      "qcom-hidma", dmadev->lldev);
-	if (rc)
-		goto uninit;
+	platform_set_drvdata(pdev, dmadev);
+	if (msi)
+		rc = hidma_request_msi(dmadev, pdev);
+
+	if (!msi || rc) {
+		hidma_ll_setup_irq(dmadev->lldev, false);
+		rc = devm_request_irq(&pdev->dev, chirq, hidma_chirq_handler,
+				      0, "qcom-hidma", dmadev->lldev);
+		if (rc)
+			goto uninit;
+	}
 
 	INIT_LIST_HEAD(&dmadev->ddev.channels);
 	rc = hidma_chan_init(dmadev, 0);
@@ -707,7 +825,6 @@ static int hidma_probe(struct platform_device *pdev)
 	hidma_debug_init(dmadev);
 	hidma_create_sysfs_entry(dmadev, "chid", S_IRUGO);
 	dev_info(&pdev->dev, "HI-DMA engine driver registration complete\n");
-	platform_set_drvdata(pdev, dmadev);
 	pm_runtime_mark_last_busy(dmadev->ddev.dev);
 	pm_runtime_put_autosuspend(dmadev->ddev.dev);
 	return 0;
@@ -746,12 +863,14 @@ static int hidma_remove(struct platform_device *pdev)
 #if IS_ENABLED(CONFIG_ACPI)
 static const struct acpi_device_id hidma_acpi_ids[] = {
 	{"QCOM8061"},
+	{"QCOM8062"},
 	{},
 };
 #endif
 
 static const struct of_device_id hidma_match[] = {
 	{.compatible = "qcom,hidma-1.0",},
+	{.compatible = "qcom,hidma-1.1",},
 	{},
 };
 MODULE_DEVICE_TABLE(of, hidma_match);
diff --git a/drivers/dma/qcom/hidma.h b/drivers/dma/qcom/hidma.h
index 181f7e0..05f8ba4 100644
--- a/drivers/dma/qcom/hidma.h
+++ b/drivers/dma/qcom/hidma.h
@@ -115,6 +115,7 @@ struct hidma_dev {
 	int				irq;
 	int				chidx;
 	u32				nr_descriptors;
+	int				msi_virqbase;
 
 	struct hidma_lldev		*lldev;
 	void				__iomem *dev_trca;
@@ -153,6 +154,7 @@ struct hidma_lldev *hidma_ll_init(struct device *dev, u32 max_channels,
 			u8 chidx);
 int hidma_ll_uninit(struct hidma_lldev *llhndl);
 irqreturn_t hidma_ll_inthandler(int irq, void *arg);
+irqreturn_t hidma_ll_inthandler_msi(int irq, void *arg, int cause);
 void hidma_cleanup_pending_tre(struct hidma_lldev *llhndl, u8 err_info,
 				u8 err_code);
 int hidma_debug_init(struct hidma_dev *dmadev);
diff --git a/drivers/dma/qcom/hidma_ll.c b/drivers/dma/qcom/hidma_ll.c
index 386a64c..5518d82 100644
--- a/drivers/dma/qcom/hidma_ll.c
+++ b/drivers/dma/qcom/hidma_ll.c
@@ -424,6 +424,14 @@ irqreturn_t hidma_ll_inthandler(int chirq, void *arg)
 	return IRQ_HANDLED;
 }
 
+irqreturn_t hidma_ll_inthandler_msi(int chirq, void *arg, int cause)
+{
+	struct hidma_lldev *lldev = arg;
+
+	hidma_ll_int_handler_internal(lldev, cause);
+	return IRQ_HANDLED;
+}
+
 int hidma_ll_enable(struct hidma_lldev *lldev)
 {
 	u32 val;
-- 
1.9.1

^ permalink raw reply related

* [PATCH V4 09/10] dmaengine: qcom_hidma: protect common data structures
From: Sinan Kaya @ 2016-09-29  2:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475115167-5898-1-git-send-email-okaya@codeaurora.org>

When MSI interrupts are supported, error and the transfer interrupt can
come from multiple processor contexts.

Each error interrupt is an MSI interrupt. If the channel is disabled by
the first error interrupt, the remaining error interrupts will gracefully
return in the interrupt handler.

If an error is observed while servicing the completions in success case,
the posting of the completions will be aborted as soon as channel disabled
state is observed. The error interrupt handler will take it from there and
finish the remaining completions. We don't want to create multiple success
and error messages to be delivered to the client in mixed order.

Also got rid of hidma_post_completed method and moved the locks inside
hidma_ll_int_handler_internal function. Rearranged the assignments so that
variables are updated only when a lock is held.

Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
---
 drivers/dma/qcom/hidma_ll.c | 142 ++++++++++++++++++--------------------------
 1 file changed, 58 insertions(+), 84 deletions(-)

diff --git a/drivers/dma/qcom/hidma_ll.c b/drivers/dma/qcom/hidma_ll.c
index f0630e0..386a64c 100644
--- a/drivers/dma/qcom/hidma_ll.c
+++ b/drivers/dma/qcom/hidma_ll.c
@@ -198,18 +198,50 @@ static void hidma_ll_tre_complete(unsigned long arg)
 	}
 }
 
-static int hidma_post_completed(struct hidma_lldev *lldev, int tre_iterator,
-				u8 err_info, u8 err_code)
+/*
+ * Called to handle the interrupt for the channel.
+ * Return a positive number if TRE or EVRE were consumed on this run.
+ * Return a positive number if there are pending TREs or EVREs.
+ * Return 0 if there is nothing to consume or no pending TREs/EVREs found.
+ */
+static int hidma_handle_tre_completion(struct hidma_lldev *lldev, u8 err_info,
+				       u8 err_code)
 {
+	u32 *current_evre;
 	struct hidma_tre *tre;
 	unsigned long flags;
+	u32 evre_write_off;
+	u32 cfg;
+	u32 offset;
+
+	evre_write_off = readl_relaxed(lldev->evca + HIDMA_EVCA_WRITE_PTR_REG);
+	if ((evre_write_off > lldev->evre_ring_size) ||
+			(evre_write_off % HIDMA_EVRE_SIZE)) {
+		dev_err(lldev->dev, "HW reports invalid EVRE write offset\n");
+		return -EINVAL;
+	}
 
 	spin_lock_irqsave(&lldev->lock, flags);
-	tre = lldev->pending_tre_list[tre_iterator / HIDMA_TRE_SIZE];
+	if (lldev->evre_processed_off == evre_write_off) {
+		spin_unlock_irqrestore(&lldev->lock, flags);
+		return 0;
+	}
+	current_evre = lldev->evre_ring + lldev->evre_processed_off;
+	cfg = current_evre[HIDMA_EVRE_CFG_IDX];
+	if (!err_info) {
+		err_info = cfg >> HIDMA_EVRE_ERRINFO_BIT_POS;
+		err_info &= HIDMA_EVRE_ERRINFO_MASK;
+	}
+	if (!err_code)
+		err_code = (cfg >> HIDMA_EVRE_CODE_BIT_POS) &
+					HIDMA_EVRE_CODE_MASK;
+
+	offset = lldev->tre_processed_off;
+	tre = lldev->pending_tre_list[offset / HIDMA_TRE_SIZE];
 	if (!tre) {
 		spin_unlock_irqrestore(&lldev->lock, flags);
 		dev_warn(lldev->dev, "tre_index [%d] and tre out of sync\n",
-			 tre_iterator / HIDMA_TRE_SIZE);
+			 lldev->tre_processed_off / HIDMA_TRE_SIZE);
 		return -EINVAL;
 	}
 	lldev->pending_tre_list[tre->tre_index] = NULL;
@@ -223,6 +255,14 @@ static int hidma_post_completed(struct hidma_lldev *lldev, int tre_iterator,
 		atomic_set(&lldev->pending_tre_count, 0);
 	}
 
+
+	HIDMA_INCREMENT_ITERATOR(lldev->tre_processed_off, HIDMA_TRE_SIZE,
+				 lldev->tre_ring_size);
+	HIDMA_INCREMENT_ITERATOR(lldev->evre_processed_off, HIDMA_EVRE_SIZE,
+				 lldev->evre_ring_size);
+
+	writel(lldev->evre_processed_off,
+			lldev->evca + HIDMA_EVCA_DOORBELL_REG);
 	spin_unlock_irqrestore(&lldev->lock, flags);
 
 	tre->err_info = err_info;
@@ -232,86 +272,7 @@ static int hidma_post_completed(struct hidma_lldev *lldev, int tre_iterator,
 	kfifo_put(&lldev->handoff_fifo, tre);
 	tasklet_schedule(&lldev->task);
 
-	return 0;
-}
-
-/*
- * Called to handle the interrupt for the channel.
- * Return a positive number if TRE or EVRE were consumed on this run.
- * Return a positive number if there are pending TREs or EVREs.
- * Return 0 if there is nothing to consume or no pending TREs/EVREs found.
- */
-static int hidma_handle_tre_completion(struct hidma_lldev *lldev, u8 err_info,
-				       u8 err_code)
-{
-	u32 evre_ring_size = lldev->evre_ring_size;
-	u32 tre_ring_size = lldev->tre_ring_size;
-	u32 tre_iterator, evre_iterator;
-	u32 num_completed = 0;
-
-	evre_write_off = readl_relaxed(lldev->evca + HIDMA_EVCA_WRITE_PTR_REG);
-	tre_iterator = lldev->tre_processed_off;
-	evre_iterator = lldev->evre_processed_off;
-
-	if ((evre_write_off > evre_ring_size) ||
-	    (evre_write_off % HIDMA_EVRE_SIZE)) {
-		dev_err(lldev->dev, "HW reports invalid EVRE write offset\n");
-		return 0;
-	}
-
-	/*
-	 * By the time control reaches here the number of EVREs and TREs
-	 * may not match. Only consume the ones that hardware told us.
-	 */
-	while ((evre_iterator != evre_write_off)) {
-		u32 *current_evre = lldev->evre_ring + evre_iterator;
-		u32 cfg;
-
-		cfg = current_evre[HIDMA_EVRE_CFG_IDX];
-		if (!err_info) {
-			err_info = cfg >> HIDMA_EVRE_ERRINFO_BIT_POS;
-			err_info &= HIDMA_EVRE_ERRINFO_MASK;
-		}
-		if (!err_code)
-			err_code = (cfg >> HIDMA_EVRE_CODE_BIT_POS) &
-					HIDMA_EVRE_CODE_MASK;
-
-		if (hidma_post_completed(lldev, tre_iterator, err_info,
-					 err_code))
-			break;
-
-		HIDMA_INCREMENT_ITERATOR(tre_iterator, HIDMA_TRE_SIZE,
-					 tre_ring_size);
-		HIDMA_INCREMENT_ITERATOR(evre_iterator, HIDMA_EVRE_SIZE,
-					 evre_ring_size);
-
-		/*
-		 * Read the new event descriptor written by the HW.
-		 * As we are processing the delivered events, other events
-		 * get queued to the SW for processing.
-		 */
-		evre_write_off =
-		    readl_relaxed(lldev->evca + HIDMA_EVCA_WRITE_PTR_REG);
-		num_completed++;
-	}
-
-	if (num_completed) {
-		u32 evre_read_off = (lldev->evre_processed_off +
-				     HIDMA_EVRE_SIZE * num_completed);
-		u32 tre_read_off = (lldev->tre_processed_off +
-				    HIDMA_TRE_SIZE * num_completed);
-
-		evre_read_off = evre_read_off % evre_ring_size;
-		tre_read_off = tre_read_off % tre_ring_size;
-
-		writel(evre_read_off, lldev->evca + HIDMA_EVCA_DOORBELL_REG);
-
-		/* record the last processed tre offset */
-		lldev->tre_processed_off = tre_read_off;
-		lldev->evre_processed_off = evre_read_off;
-	}
-
-	return num_completed;
+	return 1;
 }
 
 void hidma_cleanup_pending_tre(struct hidma_lldev *lldev, u8 err_info,
@@ -399,6 +360,16 @@ static int hidma_ll_reset(struct hidma_lldev *lldev)
  */
 static void hidma_ll_int_handler_internal(struct hidma_lldev *lldev, int cause)
 {
+	if ((lldev->trch_state == HIDMA_CH_DISABLED) ||
+		(lldev->evch_state == HIDMA_CH_DISABLED)) {
+		dev_err(lldev->dev, "error 0x%x, already disabled...\n",
+			cause);
+
+		/* Clear out pending interrupts */
+		writel(cause, lldev->evca + HIDMA_EVCA_IRQ_CLR_REG);
+		return;
+	}
+
 	if (cause & HIDMA_ERR_INT_MASK) {
 		dev_err(lldev->dev, "error 0x%x, disabling...\n",
 				cause);
@@ -430,6 +401,9 @@ static void hidma_ll_int_handler_internal(struct hidma_lldev *lldev, int cause)
 		 */
 		if (hidma_handle_tre_completion(lldev, 0, 0))
 			break;
+		if ((lldev->trch_state == HIDMA_CH_DISABLED) ||
+				(lldev->evch_state == HIDMA_CH_DISABLED))
+			break;
 	}
 
 	/* We consumed TREs or there are pending TREs or EVREs. */
-- 
1.9.1

^ permalink raw reply related

* [PATCH V4 08/10] dmaengine: qcom_hidma: add a common API to setup the interrupt
From: Sinan Kaya @ 2016-09-29  2:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475115167-5898-1-git-send-email-okaya@codeaurora.org>

Introducing the hidma_ll_setup_irq function to set up the interrupt
type externally from the OS interface.

Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
---
 drivers/dma/qcom/hidma.h    |  2 ++
 drivers/dma/qcom/hidma_ll.c | 27 +++++++++++++++++++++++----
 2 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/qcom/hidma.h b/drivers/dma/qcom/hidma.h
index 3f2ddd4..181f7e0 100644
--- a/drivers/dma/qcom/hidma.h
+++ b/drivers/dma/qcom/hidma.h
@@ -46,6 +46,7 @@ struct hidma_tre {
 };
 
 struct hidma_lldev {
+	bool msi_support;		/* flag indicating MSI support    */
 	bool initialized;		/* initialized flag               */
 	u8 trch_state;			/* trch_state of the device	  */
 	u8 evch_state;			/* evch_state of the device	  */
@@ -145,6 +146,7 @@ int hidma_ll_disable(struct hidma_lldev *lldev);
 int hidma_ll_enable(struct hidma_lldev *llhndl);
 void hidma_ll_set_transfer_params(struct hidma_lldev *llhndl, u32 tre_ch,
 	dma_addr_t src, dma_addr_t dest, u32 len, u32 flags);
+void hidma_ll_setup_irq(struct hidma_lldev *lldev, bool msi);
 int hidma_ll_setup(struct hidma_lldev *lldev);
 struct hidma_lldev *hidma_ll_init(struct device *dev, u32 max_channels,
 			void __iomem *trca, void __iomem *evca,
diff --git a/drivers/dma/qcom/hidma_ll.c b/drivers/dma/qcom/hidma_ll.c
index 088935f..f0630e0 100644
--- a/drivers/dma/qcom/hidma_ll.c
+++ b/drivers/dma/qcom/hidma_ll.c
@@ -672,17 +672,36 @@ int hidma_ll_setup(struct hidma_lldev *lldev)
 	writel(HIDMA_EVRE_SIZE * nr_tres,
 			lldev->evca + HIDMA_EVCA_RING_LEN_REG);
 
-	/* support IRQ only for now */
+	/* configure interrupts */
+	hidma_ll_setup_irq(lldev, lldev->msi_support);
+
+	rc = hidma_ll_enable(lldev);
+	if (rc)
+		return rc;
+
+	return rc;
+}
+
+void hidma_ll_setup_irq(struct hidma_lldev *lldev, bool msi)
+{
+	u32 val;
+
+	lldev->msi_support = msi;
+
+	/* disable interrupts again after reset */
+	writel(0, lldev->evca + HIDMA_EVCA_IRQ_CLR_REG);
+	writel(0, lldev->evca + HIDMA_EVCA_IRQ_EN_REG);
+
+	/* support IRQ by default */
 	val = readl(lldev->evca + HIDMA_EVCA_INTCTRL_REG);
 	val &= ~0xF;
-	val |= 0x1;
+	if (!lldev->msi_support)
+		val = val | 0x1;
 	writel(val, lldev->evca + HIDMA_EVCA_INTCTRL_REG);
 
 	/* clear all pending interrupts and enable them */
 	writel(ENABLE_IRQS, lldev->evca + HIDMA_EVCA_IRQ_CLR_REG);
 	writel(ENABLE_IRQS, lldev->evca + HIDMA_EVCA_IRQ_EN_REG);
-
-	return hidma_ll_enable(lldev);
 }
 
 struct hidma_lldev *hidma_ll_init(struct device *dev, u32 nr_tres,
-- 
1.9.1

^ permalink raw reply related

* [PATCH V4 07/10] dmaengine: qcom_hidma: bring out interrupt cause
From: Sinan Kaya @ 2016-09-29  2:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475115167-5898-1-git-send-email-okaya@codeaurora.org>

Bring out the interrupt cause to the top level so that MSI interrupts
can be hooked at a later stage.

Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
---
 drivers/dma/qcom/hidma_ll.c | 72 +++++++++++++++++++++------------------------
 1 file changed, 34 insertions(+), 38 deletions(-)

diff --git a/drivers/dma/qcom/hidma_ll.c b/drivers/dma/qcom/hidma_ll.c
index c911ca2..088935f 100644
--- a/drivers/dma/qcom/hidma_ll.c
+++ b/drivers/dma/qcom/hidma_ll.c
@@ -397,12 +397,24 @@ static int hidma_ll_reset(struct hidma_lldev *lldev)
  * requests traditionally to the destination, this concept does not apply
  * here for this HW.
  */
-irqreturn_t hidma_ll_inthandler(int chirq, void *arg)
+static void hidma_ll_int_handler_internal(struct hidma_lldev *lldev, int cause)
 {
-	struct hidma_lldev *lldev = arg;
-	u32 status;
-	u32 enable;
-	u32 cause;
+	if (cause & HIDMA_ERR_INT_MASK) {
+		dev_err(lldev->dev, "error 0x%x, disabling...\n",
+				cause);
+
+		/* Clear out pending interrupts */
+		writel(cause, lldev->evca + HIDMA_EVCA_IRQ_CLR_REG);
+
+		/* No further submissions. */
+		hidma_ll_disable(lldev);
+
+		/* Driver completes the txn and intimates the client.*/
+		hidma_cleanup_pending_tre(lldev, 0xFF,
+					  HIDMA_EVRE_STATUS_ERROR);
+
+		return;
+	}
 
 	/*
 	 * Fine tuned for this HW...
@@ -412,45 +424,29 @@ irqreturn_t hidma_ll_inthandler(int chirq, void *arg)
 	 * interrupt delivery guarantees. Do not copy this code blindly and
 	 * expect that to work.
 	 */
-	status = readl_relaxed(lldev->evca + HIDMA_EVCA_IRQ_STAT_REG);
-	enable = readl_relaxed(lldev->evca + HIDMA_EVCA_IRQ_EN_REG);
-	cause = status & enable;
-
-	while (cause) {
-		if (cause & HIDMA_ERR_INT_MASK) {
-			dev_err(lldev->dev, "error 0x%x, disabling...\n",
-					cause);
-
-			/* Clear out pending interrupts */
-			writel(cause, lldev->evca + HIDMA_EVCA_IRQ_CLR_REG);
-
-			/* No further submissions. */
-			hidma_ll_disable(lldev);
-
-			/* Driver completes the txn and intimates the client.*/
-			hidma_cleanup_pending_tre(lldev, 0xFF,
-						  HIDMA_EVRE_STATUS_ERROR);
-			goto out;
-		}
-
+	while (atomic_read(&lldev->pending_tre_count)) {
 		/*
 		 * Try to consume as many EVREs as possible.
 		 */
-		hidma_handle_tre_completion(lldev);
+		if (hidma_handle_tre_completion(lldev, 0, 0))
+			break;
+	}
 
-		/* We consumed TREs or there are pending TREs or EVREs. */
-		writel_relaxed(cause, lldev->evca + HIDMA_EVCA_IRQ_CLR_REG);
+	/* We consumed TREs or there are pending TREs or EVREs. */
+	writel_relaxed(cause, lldev->evca + HIDMA_EVCA_IRQ_CLR_REG);
+}
 
-		/*
-		 * Another interrupt might have arrived while we are
-		 * processing this one. Read the new cause.
-		 */
-		status = readl_relaxed(lldev->evca + HIDMA_EVCA_IRQ_STAT_REG);
-		enable = readl_relaxed(lldev->evca + HIDMA_EVCA_IRQ_EN_REG);
-		cause = status & enable;
-	}
+irqreturn_t hidma_ll_inthandler(int chirq, void *arg)
+{
+	struct hidma_lldev *lldev = arg;
+	u32 status;
+	u32 enable;
+	u32 cause;
 
-out:
+	status = readl_relaxed(lldev->evca + HIDMA_EVCA_IRQ_STAT_REG);
+	enable = readl_relaxed(lldev->evca + HIDMA_EVCA_IRQ_EN_REG);
+	cause = status & enable;
+	hidma_ll_int_handler_internal(lldev, cause);
 	return IRQ_HANDLED;
 }
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH V4 06/10] dmaengine: qcom_hidma: make error and success path common
From: Sinan Kaya @ 2016-09-29  2:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475115167-5898-1-git-send-email-okaya@codeaurora.org>

Remove code duplication by feeding the error code from outside
into successful data transfer handler.

Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
---
 drivers/dma/qcom/hidma_ll.c | 34 ++++++++++------------------------
 1 file changed, 10 insertions(+), 24 deletions(-)

diff --git a/drivers/dma/qcom/hidma_ll.c b/drivers/dma/qcom/hidma_ll.c
index 29fef4f..c911ca2 100644
--- a/drivers/dma/qcom/hidma_ll.c
+++ b/drivers/dma/qcom/hidma_ll.c
@@ -241,11 +241,11 @@ static int hidma_post_completed(struct hidma_lldev *lldev, int tre_iterator,
  * Return a positive number if there are pending TREs or EVREs.
  * Return 0 if there is nothing to consume or no pending TREs/EVREs found.
  */
-static int hidma_handle_tre_completion(struct hidma_lldev *lldev)
+static int hidma_handle_tre_completion(struct hidma_lldev *lldev, u8 err_info,
+				       u8 err_code)
 {
 	u32 evre_ring_size = lldev->evre_ring_size;
 	u32 tre_ring_size = lldev->tre_ring_size;
-	u32 err_info, err_code, evre_write_off;
 	u32 tre_iterator, evre_iterator;
 	u32 num_completed = 0;
 
@@ -268,10 +268,13 @@ static int hidma_handle_tre_completion(struct hidma_lldev *lldev)
 		u32 cfg;
 
 		cfg = current_evre[HIDMA_EVRE_CFG_IDX];
-		err_info = cfg >> HIDMA_EVRE_ERRINFO_BIT_POS;
-		err_info &= HIDMA_EVRE_ERRINFO_MASK;
-		err_code =
-		    (cfg >> HIDMA_EVRE_CODE_BIT_POS) & HIDMA_EVRE_CODE_MASK;
+		if (!err_info) {
+			err_info = cfg >> HIDMA_EVRE_ERRINFO_BIT_POS;
+			err_info &= HIDMA_EVRE_ERRINFO_MASK;
+		}
+		if (!err_code)
+			err_code = (cfg >> HIDMA_EVRE_CODE_BIT_POS) &
+					HIDMA_EVRE_CODE_MASK;
 
 		if (hidma_post_completed(lldev, tre_iterator, err_info,
 					 err_code))
@@ -314,27 +317,10 @@ static int hidma_handle_tre_completion(struct hidma_lldev *lldev)
 void hidma_cleanup_pending_tre(struct hidma_lldev *lldev, u8 err_info,
 			       u8 err_code)
 {
-	u32 tre_iterator;
-	u32 tre_ring_size = lldev->tre_ring_size;
-	int num_completed = 0;
-	u32 tre_read_off;
-
-	tre_iterator = lldev->tre_processed_off;
 	while (atomic_read(&lldev->pending_tre_count)) {
-		if (hidma_post_completed(lldev, tre_iterator, err_info,
-					 err_code))
+		if (hidma_handle_tre_completion(lldev, err_info, err_code))
 			break;
-		HIDMA_INCREMENT_ITERATOR(tre_iterator, HIDMA_TRE_SIZE,
-					 tre_ring_size);
-		num_completed++;
 	}
-	tre_read_off = (lldev->tre_processed_off +
-			HIDMA_TRE_SIZE * num_completed);
-
-	tre_read_off = tre_read_off % tre_ring_size;
-
-	/* record the last processed tre offset */
-	lldev->tre_processed_off = tre_read_off;
 }
 
 static int hidma_ll_reset(struct hidma_lldev *lldev)
-- 
1.9.1

^ permalink raw reply related

* [PATCH V4 05/10] dmaengine: qcom_hidma: make pending_tre_count atomic
From: Sinan Kaya @ 2016-09-29  2:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475115167-5898-1-git-send-email-okaya@codeaurora.org>

Getting ready for the MSI interrupts. The pending_tre_count is used
in the interrupt handler to make sure all outstanding requests are
serviced.

Making it atomic so that it can be updated from multiple contexts.

Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
---
 drivers/dma/qcom/hidma.h     |  2 +-
 drivers/dma/qcom/hidma_dbg.c |  3 ++-
 drivers/dma/qcom/hidma_ll.c  | 13 ++++++-------
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/dma/qcom/hidma.h b/drivers/dma/qcom/hidma.h
index e52e207..3f2ddd4 100644
--- a/drivers/dma/qcom/hidma.h
+++ b/drivers/dma/qcom/hidma.h
@@ -58,7 +58,7 @@ struct hidma_lldev {
 	void __iomem *evca;		/* Event Channel address          */
 	struct hidma_tre
 		**pending_tre_list;	/* Pointers to pending TREs	  */
-	s32 pending_tre_count;		/* Number of TREs pending	  */
+	atomic_t pending_tre_count;	/* Number of TREs pending	  */
 
 	void *tre_ring;			/* TRE ring			  */
 	dma_addr_t tre_dma;		/* TRE ring to be shared with HW  */
diff --git a/drivers/dma/qcom/hidma_dbg.c b/drivers/dma/qcom/hidma_dbg.c
index fa827e5..87db285 100644
--- a/drivers/dma/qcom/hidma_dbg.c
+++ b/drivers/dma/qcom/hidma_dbg.c
@@ -74,7 +74,8 @@ static void hidma_ll_devstats(struct seq_file *s, void *llhndl)
 	seq_printf(s, "tre_ring_handle=%pap\n", &lldev->tre_dma);
 	seq_printf(s, "tre_ring_size = 0x%x\n", lldev->tre_ring_size);
 	seq_printf(s, "tre_processed_off = 0x%x\n", lldev->tre_processed_off);
-	seq_printf(s, "pending_tre_count=%d\n", lldev->pending_tre_count);
+	seq_printf(s, "pending_tre_count=%d\n",
+			atomic_read(&lldev->pending_tre_count));
 	seq_printf(s, "evca=%p\n", lldev->evca);
 	seq_printf(s, "evre_ring=%p\n", lldev->evre_ring);
 	seq_printf(s, "evre_ring_handle=%pap\n", &lldev->evre_dma);
diff --git a/drivers/dma/qcom/hidma_ll.c b/drivers/dma/qcom/hidma_ll.c
index 3224f24..29fef4f 100644
--- a/drivers/dma/qcom/hidma_ll.c
+++ b/drivers/dma/qcom/hidma_ll.c
@@ -218,10 +218,9 @@ static int hidma_post_completed(struct hidma_lldev *lldev, int tre_iterator,
 	 * Keep track of pending TREs that SW is expecting to receive
 	 * from HW. We got one now. Decrement our counter.
 	 */
-	lldev->pending_tre_count--;
-	if (lldev->pending_tre_count < 0) {
+	if (atomic_dec_return(&lldev->pending_tre_count) < 0) {
 		dev_warn(lldev->dev, "tre count mismatch on completion");
-		lldev->pending_tre_count = 0;
+		atomic_set(&lldev->pending_tre_count, 0);
 	}
 
 	spin_unlock_irqrestore(&lldev->lock, flags);
@@ -321,7 +320,7 @@ void hidma_cleanup_pending_tre(struct hidma_lldev *lldev, u8 err_info,
 	u32 tre_read_off;
 
 	tre_iterator = lldev->tre_processed_off;
-	while (lldev->pending_tre_count) {
+	while (atomic_read(&lldev->pending_tre_count)) {
 		if (hidma_post_completed(lldev, tre_iterator, err_info,
 					 err_code))
 			break;
@@ -548,7 +547,7 @@ void hidma_ll_queue_request(struct hidma_lldev *lldev, u32 tre_ch)
 	tre->err_code = 0;
 	tre->err_info = 0;
 	tre->queued = 1;
-	lldev->pending_tre_count++;
+	atomic_inc(&lldev->pending_tre_count);
 	lldev->tre_write_offset = (lldev->tre_write_offset + HIDMA_TRE_SIZE)
 					% lldev->tre_ring_size;
 	spin_unlock_irqrestore(&lldev->lock, flags);
@@ -654,7 +653,7 @@ int hidma_ll_setup(struct hidma_lldev *lldev)
 	u32 val;
 	u32 nr_tres = lldev->nr_tres;
 
-	lldev->pending_tre_count = 0;
+	atomic_set(&lldev->pending_tre_count, 0);
 	lldev->tre_processed_off = 0;
 	lldev->evre_processed_off = 0;
 	lldev->tre_write_offset = 0;
@@ -816,7 +815,7 @@ int hidma_ll_uninit(struct hidma_lldev *lldev)
 	tasklet_kill(&lldev->task);
 	memset(lldev->trepool, 0, required_bytes);
 	lldev->trepool = NULL;
-	lldev->pending_tre_count = 0;
+	atomic_set(&lldev->pending_tre_count, 0);
 	lldev->tre_write_offset = 0;
 
 	rc = hidma_ll_reset(lldev);
-- 
1.9.1

^ permalink raw reply related


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