diff mbox series

[v2,6/7] dmaengine: sh: rz-dmac: Add RZ/V2H(P) support

Message ID 20250212221305.431716-7-fabrizio.castro.jz@renesas.com (mailing list archive)
State Superseded
Delegated to: Geert Uytterhoeven
Headers show
Series Add DMAC support to the RZ/V2H(P) | expand

Commit Message

Fabrizio Castro Feb. 12, 2025, 10:13 p.m. UTC
The DMAC IP found on the Renesas RZ/V2H(P) family of SoCs is
similar to the version found on the Renesas RZ/G2L family of
SoCs, but there are some differences:
* It only uses one register area
* It only uses one clock
* It only uses one reset
* Instead of using MID/IRD it uses REQ NO/ACK NO
* It is connected to the Interrupt Control Unit (ICU)
* On the RZ/G2L there is only 1 DMAC, on the RZ/V2H(P) there are 5

Add specific support for the Renesas RZ/V2H(P) family of SoC by
tackling the aforementioned differences.

Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
---
v1->v2:
* Switched to new macros for minimum values.
---
 drivers/dma/sh/Kconfig   |   1 +
 drivers/dma/sh/rz-dmac.c | 164 +++++++++++++++++++++++++++++++++++----
 2 files changed, 150 insertions(+), 15 deletions(-)

Comments

kernel test robot Feb. 13, 2025, 1:24 p.m. UTC | #1
Hi Fabrizio,

kernel test robot noticed the following build warnings:

[auto build test WARNING on vkoul-dmaengine/next]
[also build test WARNING on geert-renesas-drivers/renesas-clk robh/for-next linus/master v6.14-rc2 next-20250213]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Fabrizio-Castro/clk-renesas-r9a09g057-Add-entries-for-the-DMACs/20250213-061714
base:   https://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git next
patch link:    https://lore.kernel.org/r/20250212221305.431716-7-fabrizio.castro.jz%40renesas.com
patch subject: [PATCH v2 6/7] dmaengine: sh: rz-dmac: Add RZ/V2H(P) support
config: powerpc64-randconfig-001-20250213 (https://download.01.org/0day-ci/archive/20250213/202502132123.1ePmN98r-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250213/202502132123.1ePmN98r-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502132123.1ePmN98r-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/dma/sh/rz-dmac.c:979:15: warning: cast to smaller integer type 'enum rz_dmac_type' from 'const void *' [-Wvoid-pointer-to-enum-cast]
     979 |         dmac->type = (enum rz_dmac_type)of_device_get_match_data(dmac->dev);
         |                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   1 warning generated.


vim +979 drivers/dma/sh/rz-dmac.c

   962	
   963	static int rz_dmac_probe(struct platform_device *pdev)
   964	{
   965		const char *irqname = "error";
   966		struct dma_device *engine;
   967		struct rz_dmac *dmac;
   968		int channel_num;
   969		int ret;
   970		int irq;
   971		u8 i;
   972	
   973		dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL);
   974		if (!dmac)
   975			return -ENOMEM;
   976	
   977		dmac->dev = &pdev->dev;
   978		platform_set_drvdata(pdev, dmac);
 > 979		dmac->type = (enum rz_dmac_type)of_device_get_match_data(dmac->dev);
   980	
   981		ret = rz_dmac_parse_of(&pdev->dev, dmac);
   982		if (ret < 0)
   983			return ret;
   984	
   985		dmac->channels = devm_kcalloc(&pdev->dev, dmac->n_channels,
   986					      sizeof(*dmac->channels), GFP_KERNEL);
   987		if (!dmac->channels)
   988			return -ENOMEM;
   989	
   990		/* Request resources */
   991		dmac->base = devm_platform_ioremap_resource(pdev, 0);
   992		if (IS_ERR(dmac->base))
   993			return PTR_ERR(dmac->base);
   994	
   995		if (dmac->type == RZ_DMAC_RZG2L) {
   996			dmac->ext_base = devm_platform_ioremap_resource(pdev, 1);
   997			if (IS_ERR(dmac->ext_base))
   998				return PTR_ERR(dmac->ext_base);
   999		} else {
  1000			ret = rz_dmac_parse_of_icu(&pdev->dev, dmac);
  1001			if (ret)
  1002				return ret;
  1003		}
  1004	
  1005		/* Register interrupt handler for error */
  1006		irq = platform_get_irq_byname(pdev, irqname);
  1007		if (irq < 0)
  1008			return irq;
  1009	
  1010		ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0,
  1011				       irqname, NULL);
  1012		if (ret) {
  1013			dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n",
  1014				irq, ret);
  1015			return ret;
  1016		}
  1017	
  1018		/* Initialize the channels. */
  1019		INIT_LIST_HEAD(&dmac->engine.channels);
  1020	
  1021		dmac->rstc = devm_reset_control_array_get_optional_exclusive(&pdev->dev);
  1022		if (IS_ERR(dmac->rstc))
  1023			return dev_err_probe(&pdev->dev, PTR_ERR(dmac->rstc),
  1024					     "failed to get resets\n");
  1025	
  1026		pm_runtime_enable(&pdev->dev);
  1027		ret = pm_runtime_resume_and_get(&pdev->dev);
  1028		if (ret < 0) {
  1029			dev_err(&pdev->dev, "pm_runtime_resume_and_get failed\n");
  1030			goto err_pm_disable;
  1031		}
  1032	
  1033		ret = reset_control_deassert(dmac->rstc);
  1034		if (ret)
  1035			goto err_pm_runtime_put;
  1036	
  1037		for (i = 0; i < dmac->n_channels; i++) {
  1038			ret = rz_dmac_chan_probe(dmac, &dmac->channels[i], i);
  1039			if (ret < 0)
  1040				goto err;
  1041		}
  1042	
  1043		/* Register the DMAC as a DMA provider for DT. */
  1044		ret = of_dma_controller_register(pdev->dev.of_node, rz_dmac_of_xlate,
  1045						 NULL);
  1046		if (ret < 0)
  1047			goto err;
  1048	
  1049		/* Register the DMA engine device. */
  1050		engine = &dmac->engine;
  1051		dma_cap_set(DMA_SLAVE, engine->cap_mask);
  1052		dma_cap_set(DMA_MEMCPY, engine->cap_mask);
  1053		rz_dmac_writel(dmac, DCTRL_DEFAULT, CHANNEL_0_7_COMMON_BASE + DCTRL);
  1054		rz_dmac_writel(dmac, DCTRL_DEFAULT, CHANNEL_8_15_COMMON_BASE + DCTRL);
  1055	
  1056		engine->dev = &pdev->dev;
  1057	
  1058		engine->device_alloc_chan_resources = rz_dmac_alloc_chan_resources;
  1059		engine->device_free_chan_resources = rz_dmac_free_chan_resources;
  1060		engine->device_tx_status = dma_cookie_status;
  1061		engine->device_prep_slave_sg = rz_dmac_prep_slave_sg;
  1062		engine->device_prep_dma_memcpy = rz_dmac_prep_dma_memcpy;
  1063		engine->device_config = rz_dmac_config;
  1064		engine->device_terminate_all = rz_dmac_terminate_all;
  1065		engine->device_issue_pending = rz_dmac_issue_pending;
  1066		engine->device_synchronize = rz_dmac_device_synchronize;
  1067	
  1068		engine->copy_align = DMAENGINE_ALIGN_1_BYTE;
  1069		dma_set_max_seg_size(engine->dev, U32_MAX);
  1070	
  1071		ret = dma_async_device_register(engine);
  1072		if (ret < 0) {
  1073			dev_err(&pdev->dev, "unable to register\n");
  1074			goto dma_register_err;
  1075		}
  1076		return 0;
  1077	
  1078	dma_register_err:
  1079		of_dma_controller_free(pdev->dev.of_node);
  1080	err:
  1081		channel_num = i ? i - 1 : 0;
  1082		for (i = 0; i < channel_num; i++) {
  1083			struct rz_dmac_chan *channel = &dmac->channels[i];
  1084	
  1085			dma_free_coherent(&pdev->dev,
  1086					  sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC,
  1087					  channel->lmdesc.base,
  1088					  channel->lmdesc.base_dma);
  1089		}
  1090	
  1091		reset_control_assert(dmac->rstc);
  1092	err_pm_runtime_put:
  1093		pm_runtime_put(&pdev->dev);
  1094	err_pm_disable:
  1095		pm_runtime_disable(&pdev->dev);
  1096	
  1097		return ret;
  1098	}
  1099
Geert Uytterhoeven Feb. 13, 2025, 2:19 p.m. UTC | #2
Hi Fabrizio,

On Wed, 12 Feb 2025 at 23:13, Fabrizio Castro
<fabrizio.castro.jz@renesas.com> wrote:
> The DMAC IP found on the Renesas RZ/V2H(P) family of SoCs is
> similar to the version found on the Renesas RZ/G2L family of
> SoCs, but there are some differences:
> * It only uses one register area
> * It only uses one clock
> * It only uses one reset
> * Instead of using MID/IRD it uses REQ NO/ACK NO
> * It is connected to the Interrupt Control Unit (ICU)
> * On the RZ/G2L there is only 1 DMAC, on the RZ/V2H(P) there are 5
>
> Add specific support for the Renesas RZ/V2H(P) family of SoC by
> tackling the aforementioned differences.
>
> Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
> ---
> v1->v2:
> * Switched to new macros for minimum values.

Thanks for the update!

> --- a/drivers/dma/sh/Kconfig
> +++ b/drivers/dma/sh/Kconfig
> @@ -53,6 +53,7 @@ config RZ_DMAC
>         depends on ARCH_R7S72100 || ARCH_RZG2L || COMPILE_TEST
>         select RENESAS_DMA
>         select DMA_VIRTUAL_CHANNELS
> +       select RENESAS_RZV2H_ICU

This enables RENESAS_RZV2H_ICU unconditionally, while it is only
really needed on RZ/V2H, and not on other arm64 SoCs, or on arm32
or riscv SoCs.

>         help
>           This driver supports the general purpose DMA controller typically
>           found in the Renesas RZ SoC variants.
> diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
> index d7a4ce28040b..24a8c6a337d5 100644
> --- a/drivers/dma/sh/rz-dmac.c
> +++ b/drivers/dma/sh/rz-dmac.c
> @@ -14,6 +14,7 @@
>  #include <linux/dmaengine.h>
>  #include <linux/interrupt.h>
>  #include <linux/iopoll.h>
> +#include <linux/irqchip/irq-renesas-rzv2h.h>
>  #include <linux/list.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
> @@ -28,6 +29,11 @@
>  #include "../dmaengine.h"
>  #include "../virt-dma.h"
>
> +enum rz_dmac_type {
> +       RZ_DMAC_RZG2L,
> +       RZ_DMAC_RZV2H,

So basically these mean !has_icu respectively has_icu (more below)...

> +};
> +
>  enum  rz_dmac_prep_type {
>         RZ_DMAC_DESC_MEMCPY,
>         RZ_DMAC_DESC_SLAVE_SG,
> @@ -85,20 +91,32 @@ struct rz_dmac_chan {
>                 struct rz_lmdesc *tail;
>                 dma_addr_t base_dma;
>         } lmdesc;
> +
> +       /* RZ/V2H ICU related signals */
> +       u16 req_no;
> +       u8 ack_no;

This could be an anonymous union with mid_rid, as mid_rid is
mutually-exclusive with req_no and ack_no.

>  };

> @@ -824,6 +907,40 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
>         return 0;
>  }
>
> +static int rz_dmac_parse_of_icu(struct device *dev, struct rz_dmac *dmac)
> +{
> +       struct device_node *icu_np, *np = dev->of_node;
> +       struct of_phandle_args args;
> +       uint32_t dmac_index;
> +       int ret;
> +
> +       ret = of_parse_phandle_with_fixed_args(np, "renesas,icu", 1, 0, &args);
> +       if (ret)
> +               return ret;
> +
> +       icu_np = args.np;
> +       dmac_index = args.args[0];
> +
> +       if (dmac_index > RZV2H_MAX_DMAC_INDEX) {
> +               dev_err(dev, "DMAC index %u invalid.\n", dmac_index);
> +               ret = -EINVAL;
> +               goto free_icu_np;
> +       }
> +
> +       dmac->icu.pdev = of_find_device_by_node(icu_np);

What if the DMAC is probed before the ICU?
Is the returned pdev valid?
Will rzv2h_icu_register_dma_req_ack() crash when dereferencing priv?

> +       if (!dmac->icu.pdev) {
> +               ret = -ENODEV;
> +               goto free_icu_np;
> +       }
> +
> +       dmac->icu.dmac_index = dmac_index;
> +
> +free_icu_np:
> +       of_node_put(icu_np);
> +
> +       return ret;
> +}
> +
>  static int rz_dmac_parse_of(struct device *dev, struct rz_dmac *dmac)
>  {
>         struct device_node *np = dev->of_node;
> @@ -859,6 +976,7 @@ static int rz_dmac_probe(struct platform_device *pdev)
>
>         dmac->dev = &pdev->dev;
>         platform_set_drvdata(pdev, dmac);
> +       dmac->type = (enum rz_dmac_type)of_device_get_match_data(dmac->dev);

(uintptr_t)

But as "renesas,icu" is a required property for RZ/V2H, perhaps you
can devise the has_icu flag at runtime?

>
>         ret = rz_dmac_parse_of(&pdev->dev, dmac);
>         if (ret < 0)

Gr{oetje,eeting}s,

                        Geert
Geert Uytterhoeven Feb. 13, 2025, 2:23 p.m. UTC | #3
Hi Fabrizio,

On Thu, 13 Feb 2025 at 15:19, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> On Wed, 12 Feb 2025 at 23:13, Fabrizio Castro
> <fabrizio.castro.jz@renesas.com> wrote:
> > The DMAC IP found on the Renesas RZ/V2H(P) family of SoCs is
> > similar to the version found on the Renesas RZ/G2L family of
> > SoCs, but there are some differences:
> > * It only uses one register area
> > * It only uses one clock
> > * It only uses one reset
> > * Instead of using MID/IRD it uses REQ NO/ACK NO
> > * It is connected to the Interrupt Control Unit (ICU)
> > * On the RZ/G2L there is only 1 DMAC, on the RZ/V2H(P) there are 5
> >
> > Add specific support for the Renesas RZ/V2H(P) family of SoC by
> > tackling the aforementioned differences.
> >
> > Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
> > ---
> > v1->v2:
> > * Switched to new macros for minimum values.
>
> Thanks for the update!
>
> > --- a/drivers/dma/sh/Kconfig
> > +++ b/drivers/dma/sh/Kconfig
> > @@ -53,6 +53,7 @@ config RZ_DMAC
> >         depends on ARCH_R7S72100 || ARCH_RZG2L || COMPILE_TEST
> >         select RENESAS_DMA
> >         select DMA_VIRTUAL_CHANNELS
> > +       select RENESAS_RZV2H_ICU
>
> This enables RENESAS_RZV2H_ICU unconditionally, while it is only
> really needed on RZ/V2H, and not on other arm64 SoCs, or on arm32
> or riscv SoCs.

As ARCH_R9A09G057 already selects RENESAS_RZV2H_ICU, you could provide
a dummy rzv2h_icu_register_dma_req_ack() for the !RENESAS_RZV2H_ICU
case, or even disable all ICU-related code when it is not needed.

Gr{oetje,eeting}s,

                        Geert
Fabrizio Castro Feb. 18, 2025, 8:39 p.m. UTC | #4
Hi Geert,

Thanks for your feedback!

> From: Geert Uytterhoeven <geert@linux-m68k.org>
> Sent: 13 February 2025 14:20
> Subject: Re: [PATCH v2 6/7] dmaengine: sh: rz-dmac: Add RZ/V2H(P) support
> 
> Hi Fabrizio,
> 
> On Wed, 12 Feb 2025 at 23:13, Fabrizio Castro
> <fabrizio.castro.jz@renesas.com> wrote:
> > The DMAC IP found on the Renesas RZ/V2H(P) family of SoCs is
> > similar to the version found on the Renesas RZ/G2L family of
> > SoCs, but there are some differences:
> > * It only uses one register area
> > * It only uses one clock
> > * It only uses one reset
> > * Instead of using MID/IRD it uses REQ NO/ACK NO
> > * It is connected to the Interrupt Control Unit (ICU)
> > * On the RZ/G2L there is only 1 DMAC, on the RZ/V2H(P) there are 5
> >
> > Add specific support for the Renesas RZ/V2H(P) family of SoC by
> > tackling the aforementioned differences.
> >
> > Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
> > ---
> > v1->v2:
> > * Switched to new macros for minimum values.
> 
> Thanks for the update!
> 
> > --- a/drivers/dma/sh/Kconfig
> > +++ b/drivers/dma/sh/Kconfig
> > @@ -53,6 +53,7 @@ config RZ_DMAC
> >         depends on ARCH_R7S72100 || ARCH_RZG2L || COMPILE_TEST
> >         select RENESAS_DMA
> >         select DMA_VIRTUAL_CHANNELS
> > +       select RENESAS_RZV2H_ICU
> 
> This enables RENESAS_RZV2H_ICU unconditionally, while it is only
> really needed on RZ/V2H, and not on other arm64 SoCs, or on arm32
> or riscv SoCs.

Good point, I'll follow up on this on your other email.

> 
> >         help
> >           This driver supports the general purpose DMA controller typically
> >           found in the Renesas RZ SoC variants.
> > diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
> > index d7a4ce28040b..24a8c6a337d5 100644
> > --- a/drivers/dma/sh/rz-dmac.c
> > +++ b/drivers/dma/sh/rz-dmac.c
> > @@ -14,6 +14,7 @@
> >  #include <linux/dmaengine.h>
> >  #include <linux/interrupt.h>
> >  #include <linux/iopoll.h>
> > +#include <linux/irqchip/irq-renesas-rzv2h.h>
> >  #include <linux/list.h>
> >  #include <linux/module.h>
> >  #include <linux/of.h>
> > @@ -28,6 +29,11 @@
> >  #include "../dmaengine.h"
> >  #include "../virt-dma.h"
> >
> > +enum rz_dmac_type {
> > +       RZ_DMAC_RZG2L,
> > +       RZ_DMAC_RZV2H,
> 
> So basically these mean !has_icu respectively has_icu (more below)...

Yes.

> 
> > +};
> > +
> >  enum  rz_dmac_prep_type {
> >         RZ_DMAC_DESC_MEMCPY,
> >         RZ_DMAC_DESC_SLAVE_SG,
> > @@ -85,20 +91,32 @@ struct rz_dmac_chan {
> >                 struct rz_lmdesc *tail;
> >                 dma_addr_t base_dma;
> >         } lmdesc;
> > +
> > +       /* RZ/V2H ICU related signals */
> > +       u16 req_no;
> > +       u8 ack_no;
> 
> This could be an anonymous union with mid_rid, as mid_rid is
> mutually-exclusive with req_no and ack_no.

Indeed, I'll adjust accordingly.

> 
> >  };
> 
> > @@ -824,6 +907,40 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
> >         return 0;
> >  }
> >
> > +static int rz_dmac_parse_of_icu(struct device *dev, struct rz_dmac *dmac)
> > +{
> > +       struct device_node *icu_np, *np = dev->of_node;
> > +       struct of_phandle_args args;
> > +       uint32_t dmac_index;
> > +       int ret;
> > +
> > +       ret = of_parse_phandle_with_fixed_args(np, "renesas,icu", 1, 0, &args);
> > +       if (ret)
> > +               return ret;
> > +
> > +       icu_np = args.np;
> > +       dmac_index = args.args[0];
> > +
> > +       if (dmac_index > RZV2H_MAX_DMAC_INDEX) {
> > +               dev_err(dev, "DMAC index %u invalid.\n", dmac_index);
> > +               ret = -EINVAL;
> > +               goto free_icu_np;
> > +       }
> > +
> > +       dmac->icu.pdev = of_find_device_by_node(icu_np);
> 
> What if the DMAC is probed before the ICU?

This doesn't look like a possible scenario, as irqchips are initialized very early.

> Is the returned pdev valid?
> Will rzv2h_icu_register_dma_req_ack() crash when dereferencing priv?

Even though it doesn't seem possible for the ICU driver to get probed after the DMAC
driver, I have still looked into possible ways your comment could apply, and I have
found one, although it can't really happen in practice, as the system will hang before
getting there.

If the probing of the ICU driver _fails_, then of_find_device_by_node() returns a valid
pointer. At some point we call into rzv2h_icu_register_dma_req_ack(), and the first time
we do anything with `priv` we deal with a NULL pointer.

However, if the probing of the ICU driver fails, the system hangs very early on because
the ICU is the interrupt parent of the pintctrl node.

In order to see the failure I have described I had to take `interrupt-parent = <&icu>;`
out of the pinctrl node, on top of manually make the ICU driver fail.

If the ICU driver fails its initialization the system is gone, and not because of DMAC,
therefore I'll leave this bit unchanged for the next version of the series.

> 
> > +       if (!dmac->icu.pdev) {
> > +               ret = -ENODEV;
> > +               goto free_icu_np;
> > +       }
> > +
> > +       dmac->icu.dmac_index = dmac_index;
> > +
> > +free_icu_np:
> > +       of_node_put(icu_np);
> > +
> > +       return ret;
> > +}
> > +
> >  static int rz_dmac_parse_of(struct device *dev, struct rz_dmac *dmac)
> >  {
> >         struct device_node *np = dev->of_node;
> > @@ -859,6 +976,7 @@ static int rz_dmac_probe(struct platform_device *pdev)
> >
> >         dmac->dev = &pdev->dev;
> >         platform_set_drvdata(pdev, dmac);
> > +       dmac->type = (enum rz_dmac_type)of_device_get_match_data(dmac->dev);
> 
> (uintptr_t)
> 
> But as "renesas,icu" is a required property for RZ/V2H, perhaps you
> can devise the has_icu flag at runtime?

I'll switch to using the has_icu flag at runtime.

Thanks!

Cheers,
Fab

> 
> >
> >         ret = rz_dmac_parse_of(&pdev->dev, dmac);
> >         if (ret < 0)
> 
> Gr{oetje,eeting}s,
> 
>                         Geert
> 
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@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
Fabrizio Castro Feb. 18, 2025, 8:40 p.m. UTC | #5
Hi Geert,

Thanks for your feedback!

> From: Geert Uytterhoeven <geert@linux-m68k.org>
> Sent: 13 February 2025 14:23
> Subject: Re: [PATCH v2 6/7] dmaengine: sh: rz-dmac: Add RZ/V2H(P) support
> 
> Hi Fabrizio,
> 
> On Thu, 13 Feb 2025 at 15:19, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> > On Wed, 12 Feb 2025 at 23:13, Fabrizio Castro
> > <fabrizio.castro.jz@renesas.com> wrote:
> > > The DMAC IP found on the Renesas RZ/V2H(P) family of SoCs is
> > > similar to the version found on the Renesas RZ/G2L family of
> > > SoCs, but there are some differences:
> > > * It only uses one register area
> > > * It only uses one clock
> > > * It only uses one reset
> > > * Instead of using MID/IRD it uses REQ NO/ACK NO
> > > * It is connected to the Interrupt Control Unit (ICU)
> > > * On the RZ/G2L there is only 1 DMAC, on the RZ/V2H(P) there are 5
> > >
> > > Add specific support for the Renesas RZ/V2H(P) family of SoC by
> > > tackling the aforementioned differences.
> > >
> > > Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
> > > ---
> > > v1->v2:
> > > * Switched to new macros for minimum values.
> >
> > Thanks for the update!
> >
> > > --- a/drivers/dma/sh/Kconfig
> > > +++ b/drivers/dma/sh/Kconfig
> > > @@ -53,6 +53,7 @@ config RZ_DMAC
> > >         depends on ARCH_R7S72100 || ARCH_RZG2L || COMPILE_TEST
> > >         select RENESAS_DMA
> > >         select DMA_VIRTUAL_CHANNELS
> > > +       select RENESAS_RZV2H_ICU
> >
> > This enables RENESAS_RZV2H_ICU unconditionally, while it is only
> > really needed on RZ/V2H, and not on other arm64 SoCs, or on arm32
> > or riscv SoCs.
> 
> As ARCH_R9A09G057 already selects RENESAS_RZV2H_ICU, you could provide
> a dummy rzv2h_icu_register_dma_req_ack() for the !RENESAS_RZV2H_ICU
> case, or even disable all ICU-related code when it is not needed.

A dummy rzv2h_icu_register_dma_req_ack() sounds good, I'll include that
in the next version of the series.

Thanks!

Cheers,
Fab

> 
> Gr{oetje,eeting}s,
> 
>                         Geert
> 
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@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
diff mbox series

Patch

diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig
index 6ea5a880b433..020cf941abc7 100644
--- a/drivers/dma/sh/Kconfig
+++ b/drivers/dma/sh/Kconfig
@@ -53,6 +53,7 @@  config RZ_DMAC
 	depends on ARCH_R7S72100 || ARCH_RZG2L || COMPILE_TEST
 	select RENESAS_DMA
 	select DMA_VIRTUAL_CHANNELS
+	select RENESAS_RZV2H_ICU
 	help
 	  This driver supports the general purpose DMA controller typically
 	  found in the Renesas RZ SoC variants.
diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
index d7a4ce28040b..24a8c6a337d5 100644
--- a/drivers/dma/sh/rz-dmac.c
+++ b/drivers/dma/sh/rz-dmac.c
@@ -14,6 +14,7 @@ 
 #include <linux/dmaengine.h>
 #include <linux/interrupt.h>
 #include <linux/iopoll.h>
+#include <linux/irqchip/irq-renesas-rzv2h.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -28,6 +29,11 @@ 
 #include "../dmaengine.h"
 #include "../virt-dma.h"
 
+enum rz_dmac_type {
+	RZ_DMAC_RZG2L,
+	RZ_DMAC_RZV2H,
+};
+
 enum  rz_dmac_prep_type {
 	RZ_DMAC_DESC_MEMCPY,
 	RZ_DMAC_DESC_SLAVE_SG,
@@ -85,20 +91,32 @@  struct rz_dmac_chan {
 		struct rz_lmdesc *tail;
 		dma_addr_t base_dma;
 	} lmdesc;
+
+	/* RZ/V2H ICU related signals */
+	u16 req_no;
+	u8 ack_no;
 };
 
 #define to_rz_dmac_chan(c)	container_of(c, struct rz_dmac_chan, vc.chan)
 
+struct rz_dmac_icu {
+	struct platform_device *pdev;
+	u8 dmac_index;
+};
+
 struct rz_dmac {
 	struct dma_device engine;
 	struct device *dev;
 	struct reset_control *rstc;
+	struct rz_dmac_icu icu;
 	void __iomem *base;
 	void __iomem *ext_base;
 
 	unsigned int n_channels;
 	struct rz_dmac_chan *channels;
 
+	enum rz_dmac_type type;
+
 	DECLARE_BITMAP(modules, 1024);
 };
 
@@ -167,6 +185,22 @@  struct rz_dmac {
 #define RZ_DMAC_MAX_CHANNELS		16
 #define DMAC_NR_LMDESC			64
 
+/* RZ/V2H ICU related */
+#define RZV2H_REQ_NO_MASK		GENMASK(9, 0)
+#define RZV2H_ACK_NO_MASK		GENMASK(16, 10)
+#define RZV2H_HIEN_MASK			BIT(17)
+#define RZV2H_LVL_MASK			BIT(18)
+#define RZV2H_AM_MASK			GENMASK(21, 19)
+#define RZV2H_TM_MASK			BIT(22)
+#define RZV2H_EXTRACT_REQ_NO(x)		FIELD_GET(RZV2H_REQ_NO_MASK, (x))
+#define RZV2H_EXTRACT_ACK_NO(x)		FIELD_GET(RZV2H_ACK_NO_MASK, (x))
+#define RZVH2_EXTRACT_CHCFG(x)		((FIELD_GET(RZV2H_HIEN_MASK, (x)) << 5) | \
+					 (FIELD_GET(RZV2H_LVL_MASK, (x))  << 6) | \
+					 (FIELD_GET(RZV2H_AM_MASK, (x))   << 8) | \
+					 (FIELD_GET(RZV2H_TM_MASK, (x))   << 22))
+
+#define RZV2H_MAX_DMAC_INDEX		4
+
 /*
  * -----------------------------------------------------------------------------
  * Device access
@@ -324,7 +358,15 @@  static void rz_dmac_prepare_desc_for_memcpy(struct rz_dmac_chan *channel)
 	lmdesc->chext = 0;
 	lmdesc->header = HEADER_LV;
 
-	rz_dmac_set_dmars_register(dmac, channel->index, 0);
+	if (dmac->type == RZ_DMAC_RZG2L) {
+		rz_dmac_set_dmars_register(dmac, channel->index, 0);
+	} else {
+		rzv2h_icu_register_dma_req_ack(dmac->icu.pdev,
+					       dmac->icu.dmac_index,
+					       channel->index,
+					       RZV2H_ICU_DMAC_REQ_NO_DEFAULT,
+					       RZV2H_ICU_DMAC_ACK_NO_DEFAULT);
+	}
 
 	channel->chcfg = chcfg;
 	channel->chctrl = CHCTRL_STG | CHCTRL_SETEN;
@@ -375,7 +417,15 @@  static void rz_dmac_prepare_descs_for_slave_sg(struct rz_dmac_chan *channel)
 
 	channel->lmdesc.tail = lmdesc;
 
-	rz_dmac_set_dmars_register(dmac, channel->index, channel->mid_rid);
+	if (dmac->type == RZ_DMAC_RZG2L) {
+		rz_dmac_set_dmars_register(dmac, channel->index, channel->mid_rid);
+	} else {
+		rzv2h_icu_register_dma_req_ack(dmac->icu.pdev,
+					       dmac->icu.dmac_index,
+					       channel->index, channel->req_no,
+					       channel->ack_no);
+	}
+
 	channel->chctrl = CHCTRL_SETEN;
 }
 
@@ -452,9 +502,15 @@  static void rz_dmac_free_chan_resources(struct dma_chan *chan)
 	list_splice_tail_init(&channel->ld_active, &channel->ld_free);
 	list_splice_tail_init(&channel->ld_queue, &channel->ld_free);
 
-	if (channel->mid_rid >= 0) {
-		clear_bit(channel->mid_rid, dmac->modules);
-		channel->mid_rid = -EINVAL;
+	if (dmac->type == RZ_DMAC_RZG2L) {
+		if (channel->mid_rid >= 0) {
+			clear_bit(channel->mid_rid, dmac->modules);
+			channel->mid_rid = -EINVAL;
+		}
+	} else {
+		clear_bit(channel->req_no, dmac->modules);
+		channel->req_no = RZV2H_ICU_DMAC_REQ_NO_DEFAULT;
+		channel->ack_no = RZV2H_ICU_DMAC_ACK_NO_DEFAULT;
 	}
 
 	spin_unlock_irqrestore(&channel->vc.lock, flags);
@@ -647,7 +703,15 @@  static void rz_dmac_device_synchronize(struct dma_chan *chan)
 	if (ret < 0)
 		dev_warn(dmac->dev, "DMA Timeout");
 
-	rz_dmac_set_dmars_register(dmac, channel->index, 0);
+	if (dmac->type == RZ_DMAC_RZG2L) {
+		rz_dmac_set_dmars_register(dmac, channel->index, 0);
+	} else {
+		rzv2h_icu_register_dma_req_ack(dmac->icu.pdev,
+					       dmac->icu.dmac_index,
+					       channel->index,
+					       RZV2H_ICU_DMAC_REQ_NO_DEFAULT,
+					       RZV2H_ICU_DMAC_ACK_NO_DEFAULT);
+	}
 }
 
 /*
@@ -727,13 +791,30 @@  static bool rz_dmac_chan_filter(struct dma_chan *chan, void *arg)
 	struct rz_dmac *dmac = to_rz_dmac(chan->device);
 	struct of_phandle_args *dma_spec = arg;
 	u32 ch_cfg;
+	u16 req_no;
+
+	if (dmac->type == RZ_DMAC_RZG2L) {
+		channel->mid_rid = dma_spec->args[0] & MID_RID_MASK;
+		ch_cfg = (dma_spec->args[0] & CHCFG_MASK) >> 10;
+		channel->chcfg = CHCFG_FILL_TM(ch_cfg) | CHCFG_FILL_AM(ch_cfg) |
+				 CHCFG_FILL_LVL(ch_cfg) | CHCFG_FILL_HIEN(ch_cfg);
+
+		return !test_and_set_bit(channel->mid_rid, dmac->modules);
+	}
+
+	req_no = RZV2H_EXTRACT_REQ_NO(dma_spec->args[0]);
+	if (req_no >= RZV2H_ICU_DMAC_REQ_NO_MIN_FIX_OUTPUT)
+		return false;
+
+	channel->req_no = req_no;
+
+	channel->ack_no = RZV2H_EXTRACT_ACK_NO(dma_spec->args[0]);
+	if (channel->ack_no >= RZV2H_ICU_DMAC_ACK_NO_MIN_FIX_OUTPUT)
+		channel->ack_no = RZV2H_ICU_DMAC_ACK_NO_DEFAULT;
 
-	channel->mid_rid = dma_spec->args[0] & MID_RID_MASK;
-	ch_cfg = (dma_spec->args[0] & CHCFG_MASK) >> 10;
-	channel->chcfg = CHCFG_FILL_TM(ch_cfg) | CHCFG_FILL_AM(ch_cfg) |
-			 CHCFG_FILL_LVL(ch_cfg) | CHCFG_FILL_HIEN(ch_cfg);
+	channel->chcfg = RZVH2_EXTRACT_CHCFG(dma_spec->args[0]);
 
-	return !test_and_set_bit(channel->mid_rid, dmac->modules);
+	return !test_and_set_bit(channel->req_no, dmac->modules);
 }
 
 static struct dma_chan *rz_dmac_of_xlate(struct of_phandle_args *dma_spec,
@@ -769,6 +850,8 @@  static int rz_dmac_chan_probe(struct rz_dmac *dmac,
 
 	channel->index = index;
 	channel->mid_rid = -EINVAL;
+	channel->req_no = RZV2H_ICU_DMAC_REQ_NO_DEFAULT;
+	channel->ack_no = RZV2H_ICU_DMAC_ACK_NO_DEFAULT;
 
 	/* Request the channel interrupt. */
 	scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index);
@@ -824,6 +907,40 @@  static int rz_dmac_chan_probe(struct rz_dmac *dmac,
 	return 0;
 }
 
+static int rz_dmac_parse_of_icu(struct device *dev, struct rz_dmac *dmac)
+{
+	struct device_node *icu_np, *np = dev->of_node;
+	struct of_phandle_args args;
+	uint32_t dmac_index;
+	int ret;
+
+	ret = of_parse_phandle_with_fixed_args(np, "renesas,icu", 1, 0, &args);
+	if (ret)
+		return ret;
+
+	icu_np = args.np;
+	dmac_index = args.args[0];
+
+	if (dmac_index > RZV2H_MAX_DMAC_INDEX) {
+		dev_err(dev, "DMAC index %u invalid.\n", dmac_index);
+		ret = -EINVAL;
+		goto free_icu_np;
+	}
+
+	dmac->icu.pdev = of_find_device_by_node(icu_np);
+	if (!dmac->icu.pdev) {
+		ret = -ENODEV;
+		goto free_icu_np;
+	}
+
+	dmac->icu.dmac_index = dmac_index;
+
+free_icu_np:
+	of_node_put(icu_np);
+
+	return ret;
+}
+
 static int rz_dmac_parse_of(struct device *dev, struct rz_dmac *dmac)
 {
 	struct device_node *np = dev->of_node;
@@ -859,6 +976,7 @@  static int rz_dmac_probe(struct platform_device *pdev)
 
 	dmac->dev = &pdev->dev;
 	platform_set_drvdata(pdev, dmac);
+	dmac->type = (enum rz_dmac_type)of_device_get_match_data(dmac->dev);
 
 	ret = rz_dmac_parse_of(&pdev->dev, dmac);
 	if (ret < 0)
@@ -874,9 +992,15 @@  static int rz_dmac_probe(struct platform_device *pdev)
 	if (IS_ERR(dmac->base))
 		return PTR_ERR(dmac->base);
 
-	dmac->ext_base = devm_platform_ioremap_resource(pdev, 1);
-	if (IS_ERR(dmac->ext_base))
-		return PTR_ERR(dmac->ext_base);
+	if (dmac->type == RZ_DMAC_RZG2L) {
+		dmac->ext_base = devm_platform_ioremap_resource(pdev, 1);
+		if (IS_ERR(dmac->ext_base))
+			return PTR_ERR(dmac->ext_base);
+	} else {
+		ret = rz_dmac_parse_of_icu(&pdev->dev, dmac);
+		if (ret)
+			return ret;
+	}
 
 	/* Register interrupt handler for error */
 	irq = platform_get_irq_byname(pdev, irqname);
@@ -991,10 +1115,20 @@  static void rz_dmac_remove(struct platform_device *pdev)
 	reset_control_assert(dmac->rstc);
 	pm_runtime_put(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
+
+	if (dmac->type == RZ_DMAC_RZV2H)
+		platform_device_put(dmac->icu.pdev);
 }
 
 static const struct of_device_id of_rz_dmac_match[] = {
-	{ .compatible = "renesas,rz-dmac", },
+	{
+		.compatible	= "renesas,r9a09g057-dmac",
+		.data		= (void *) RZ_DMAC_RZV2H
+	},
+	{
+		.compatible	= "renesas,rz-dmac",
+		.data		= (void *) RZ_DMAC_RZG2L
+	},
 	{ /* Sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, of_rz_dmac_match);