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 |
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
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
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
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
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 --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);
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(-)