NVIDIA Tegra AHB DMA controller driver
Subsystems (6)
cover NVIDIA Tegra AHB DMA controller driver
NVIDIA Tegra20/30 SoC's have AHB DMA controller. It has 4 DMA channels, supports AHB <-> Memory and Memory <-> Memory transfers, slave / master modes. This driver is primarily supposed to be used by gpu/host1x in a master mode, performing 3D HW context stores.
Dmitry Osipenko (5):
clk: tegra: Add AHB DMA clock entry
clk: tegra: Bump SCLK clock rate to 216MHz on Tegra20
dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
ARM: dts: tegra: Add AHB DMA controller nodes
.../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 + arch/arm/boot/dts/tegra20.dtsi | 9 + arch/arm/boot/dts/tegra30.dtsi | 9 + drivers/clk/tegra/clk-id.h | 1 + drivers/clk/tegra/clk-tegra-periph.c | 1 + drivers/clk/tegra/clk-tegra20.c | 8 +- drivers/clk/tegra/clk-tegra30.c | 2 + drivers/dma/Kconfig | 9 + drivers/dma/Makefile | 1 + drivers/dma/tegra20-ahb-dma.c | 679 +++++++++++++++++++++ 10 files changed, 741 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt create mode 100644 drivers/dma/tegra20-ahb-dma.c
Signature · 1 line
2.14.1
PATCH 1/5 clk: tegra: Add AHB DMA clock entry
AHB DMA presents on Tegra20/30. Add missing entries, so that driver for AHB DMA could be implemented.
--- drivers/clk/tegra/clk-id.h | 1 + drivers/clk/tegra/clk-tegra-periph.c | 1 + drivers/clk/tegra/clk-tegra20.c | 6 ++++++ drivers/clk/tegra/clk-tegra30.c | 2 ++ 4 files changed, 10 insertions(+)
drivers/clk/tegra/clk-id.h +1 -0
@@ -12,6 +12,7 @@ enum clk_id { tegra_clk_amx, tegra_clk_amx1, tegra_clk_apb2ape, + tegra_clk_ahbdma, tegra_clk_apbdma, tegra_clk_apbif, tegra_clk_ape,
drivers/clk/tegra/clk-tegra-periph.c +1 -0
@@ -823,6 +823,7 @@ static struct tegra_periph_init_data gate_clks[] = {GATE ("timer" ,"clk_m" ,5 ,0 , tegra_clk_timer,CLK_IS_CRITICAL ),GATE ("isp" ,"clk_m" ,23 ,0 , tegra_clk_isp,0 ),GATE ("vcp" ,"clk_m" ,29 ,0 , tegra_clk_vcp,0 ), +GATE ("ahbdma" ,"clk_m" ,33 ,0 , tegra_clk_ahbdma,0 ),GATE ("apbdma" ,"clk_m" ,34 ,0 , tegra_clk_apbdma,0 ),GATE ("kbc" ,"clk_32k" ,36 ,TEGRA_PERIPH_ON_APB |TEGRA_PERIPH_NO_RESET , tegra_clk_kbc,0 ), GATE("fuse", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse, 0),
drivers/clk/tegra/clk-tegra20.c +6 -0
@@ -449,6 +449,7 @@ static struct tegra_devclk devclks[] __initdata = { { .con_id = "audio", .dt_id = TEGRA20_CLK_AUDIO }, { .con_id = "audio_2x", .dt_id = TEGRA20_CLK_AUDIO_2X }, { .dev_id = "tegra20-ac97", .dt_id = TEGRA20_CLK_AC97 }, + { .dev_id = "tegra-ahbdma", .dt_id = TEGRA20_CLK_AHBDMA }, { .dev_id = "tegra-apbdma", .dt_id = TEGRA20_CLK_APBDMA }, { .dev_id = "rtc-tegra", .dt_id = TEGRA20_CLK_RTC }, { .dev_id = "timer", .dt_id = TEGRA20_CLK_TIMER },
@@ -806,6 +807,11 @@ static void __init tegra20_periph_clk_init(void) clk_base, 0, 3, periph_clk_enb_refcnt); clks[TEGRA20_CLK_AC97 ] = clk; +/* ahbdma */ + clk =tegra_clk_register_periph_gate ("ahbdma" ,"hclk" ,0 , clk_base, +0 ,33 , periph_clk_enb_refcnt); + clks[TEGRA20_CLK_AHBDMA ] = clk; +/* apbdma */ clk =tegra_clk_register_periph_gate ("apbdma" ,"pclk" ,0 , clk_base,0 ,34 , periph_clk_enb_refcnt);
drivers/clk/tegra/clk-tegra30.c +2 -0
@@ -612,6 +612,7 @@ static struct tegra_devclk devclks[] __initdata = { { .con_id = "fuse_burn", .dev_id = "fuse-tegra", .dt_id = TEGRA30_CLK_FUSE_BURN }, { .con_id = "apbif", .dev_id = "tegra30-ahub", .dt_id = TEGRA30_CLK_APBIF }, { .con_id = "hda2hdmi", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA2HDMI }, + { .dev_id = "tegra-ahbdma", .dt_id = TEGRA30_CLK_AHBDMA }, { .dev_id = "tegra-apbdma", .dt_id = TEGRA30_CLK_APBDMA }, { .dev_id = "rtc-tegra", .dt_id = TEGRA30_CLK_RTC }, { .dev_id = "timer", .dt_id = TEGRA30_CLK_TIMER },
@@ -788,6 +789,7 @@ static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = { [tegra_clk_extern3] = { .dt_id = TEGRA30_CLK_EXTERN3, .present = true }, [tegra_clk_disp1] = { .dt_id = TEGRA30_CLK_DISP1, .present = true }, [tegra_clk_disp2] = { .dt_id = TEGRA30_CLK_DISP2, .present = true }, + [tegra_clk_ahbdma] = { .dt_id = TEGRA30_CLK_AHBDMA, .present = true }, [tegra_clk_apbdma] = { .dt_id = TEGRA30_CLK_APBDMA, .present = true }, [tegra_clk_rtc] = { .dt_id = TEGRA30_CLK_RTC, .present = true }, [tegra_clk_timer] = { .dt_id = TEGRA30_CLK_TIMER, .present = true },
Signature · 1 line
2.14.1
Re: [PATCH v1 1/5] clk: tegra: Add AHB DMA clock entry
Quoted 28 lines · Dmitry Osipenko
AHB DMA presents on Tegra20/30. Add missing entries, so that driver for AHB DMA could be implemented.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/clk/tegra/clk-id.h | 1 + drivers/clk/tegra/clk-tegra-periph.c | 1 + drivers/clk/tegra/clk-tegra20.c | 6 ++++++ drivers/clk/tegra/clk-tegra30.c | 2 ++ 4 files changed, 10 insertions(+)drivers/clk/tegra/clk-id.h +1
-0@@ -12,6 +12,7 @@ enum clk_id { tegra_clk_amx, tegra_clk_amx1, tegra_clk_apb2ape, + tegra_clk_ahbdma, tegra_clk_apbdma, tegra_clk_apbif, tegra_clk_ape,drivers/clk/tegra/clk-tegra-periph.c +0
-0
drivers/clk/tegra/clk-tegra-periph.c static struct tegra_periph_init_data gate_clks[] = { · line 826
Quoted 4 lines
drivers/clk/tegra/clk-tegra-periph.c +1
-0@@ -823,6 +823,7 @@ static struct tegra_periph_init_data gate_clks[] = {GATE ("timer" ,"clk_m" ,5 ,0 , tegra_clk_timer,CLK_IS_CRITICAL ),GATE ("isp" ,"clk_m" ,23 ,0 , tegra_clk_isp,0 ),GATE ("vcp" ,"clk_m" ,29 ,0 , tegra_clk_vcp,0 ), + GATE("ahbdma", "clk_m", 33, 0, tegra_clk_ahbdma, 0),
Parent for this should be hclk on Tegra30 and later chips as well..
drivers/clk/tegra/clk-tegra20.c static struct tegra_devclk devclks[] __initdata = { · line 452
Quoted 12 lines
drivers/clk/tegra/clk-tegra-periph.c +0
-0GATE ("apbdma" ,"clk_m" ,34 ,0 , tegra_clk_apbdma,0 ),GATE ("kbc" ,"clk_32k" ,36 ,TEGRA_PERIPH_ON_APB |TEGRA_PERIPH_NO_RESET , tegra_clk_kbc,0 ), GATE("fuse", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse, 0),drivers/clk/tegra/clk-tegra20.c +1
-0@@ -449,6 +449,7 @@ static struct tegra_devclk devclks[] __initdata = { { .con_id = "audio", .dt_id = TEGRA20_CLK_AUDIO }, { .con_id = "audio_2x", .dt_id = TEGRA20_CLK_AUDIO_2X }, { .dev_id = "tegra20-ac97", .dt_id = TEGRA20_CLK_AC97 }, + { .dev_id = "tegra-ahbdma", .dt_id = TEGRA20_CLK_AHBDMA },
This isn't needed if you use DT bindings to get the clock handle.
drivers/clk/tegra/clk-tegra20.c static void __init tegra20_periph_clk_init(void) · line 814
Quoted 12 lines
drivers/clk/tegra/clk-tegra20.c +5
-0{ .dev_id = "tegra-apbdma", .dt_id = TEGRA20_CLK_APBDMA }, { .dev_id = "rtc-tegra", .dt_id = TEGRA20_CLK_RTC }, { .dev_id = "timer", .dt_id = TEGRA20_CLK_TIMER },@@ -806,6 +807,11 @@ static void __init tegra20_periph_clk_init(void) clk_base, 0, 3, periph_clk_enb_refcnt); clks[TEGRA20_CLK_AC97 ] = clk; +/* ahbdma */ + clk =tegra_clk_register_periph_gate ("ahbdma" ,"hclk" ,0 , clk_base, +0 ,33 , periph_clk_enb_refcnt); + clks[TEGRA20_CLK_AHBDMA ] = clk; +
You can use the generic definition here if you correct the entry above.
drivers/clk/tegra/clk-tegra30.c static struct tegra_devclk devclks[] __initdata = { · line 615
Quoted 12 lines
drivers/clk/tegra/clk-tegra20.c +0
-0/* apbdma */ clk =tegra_clk_register_periph_gate ("apbdma" ,"pclk" ,0 , clk_base,0 ,34 , periph_clk_enb_refcnt);drivers/clk/tegra/clk-tegra30.c +1
-0@@ -612,6 +612,7 @@ static struct tegra_devclk devclks[] __initdata = { { .con_id = "fuse_burn", .dev_id = "fuse-tegra", .dt_id = TEGRA30_CLK_FUSE_BURN }, { .con_id = "apbif", .dev_id = "tegra30-ahub", .dt_id = TEGRA30_CLK_APBIF }, { .con_id = "hda2hdmi", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA2HDMI }, + { .dev_id = "tegra-ahbdma", .dt_id = TEGRA30_CLK_AHBDMA },
Same as for Tegra20.
drivers/clk/tegra/clk-tegra30.c static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = { · line 795
Quoted 11 lines
drivers/clk/tegra/clk-tegra30.c +1
-0{ .dev_id = "tegra-apbdma", .dt_id = TEGRA30_CLK_APBDMA }, { .dev_id = "rtc-tegra", .dt_id = TEGRA30_CLK_RTC }, { .dev_id = "timer", .dt_id = TEGRA30_CLK_TIMER },@@ -788,6 +789,7 @@ static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = { [tegra_clk_extern3] = { .dt_id = TEGRA30_CLK_EXTERN3, .present = true }, [tegra_clk_disp1] = { .dt_id = TEGRA30_CLK_DISP1, .present = true }, [tegra_clk_disp2] = { .dt_id = TEGRA30_CLK_DISP2, .present = true }, + [tegra_clk_ahbdma] = { .dt_id = TEGRA30_CLK_AHBDMA, .present = true }, [tegra_clk_apbdma] = { .dt_id = TEGRA30_CLK_APBDMA, .present = true }, [tegra_clk_rtc] = { .dt_id = TEGRA30_CLK_RTC, .present = true }, [tegra_clk_timer] = { .dt_id = TEGRA30_CLK_TIMER, .present = true },
Cheers,
Peter.
Re: [PATCH v1 1/5] clk: tegra: Add AHB DMA clock entry
Quoted 27 lines · Peter De Schrijver
Quoted 26 lines · Dmitry Osipenko
AHB DMA presents on Tegra20/30. Add missing entries, so that driver for AHB DMA could be implemented.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/clk/tegra/clk-id.h | 1 + drivers/clk/tegra/clk-tegra-periph.c | 1 + drivers/clk/tegra/clk-tegra20.c | 6 ++++++ drivers/clk/tegra/clk-tegra30.c | 2 ++ 4 files changed, 10 insertions(+)drivers/clk/tegra/clk-id.h +1
-0@@ -12,6 +12,7 @@ enum clk_id { tegra_clk_amx, tegra_clk_amx1, tegra_clk_apb2ape, + tegra_clk_ahbdma, tegra_clk_apbdma, tegra_clk_apbif, tegra_clk_ape,drivers/clk/tegra/clk-tegra-periph.c +0
-1--- a/drivers/clk/tegra/clk-tegra-periph.c
Quoted 9 lines
drivers/clk/tegra/clk-tegra-periph.c static struct tegra_periph_init_data gate_clks[] = { · line 826
Quoted 6 lines
+++
b/drivers/clk/tegra/clk-tegra-periph.cdrivers/clk/tegra/clk-tegra-periph.c +1
-0@@ -823,6 +823,7 @@ static struct tegra_periph_init_data gate_clks[] = {GATE ("timer" ,"clk_m" ,5 ,0 , tegra_clk_timer,CLK_IS_CRITICAL ),GATE ("isp" ,"clk_m" ,23 ,0 , tegra_clk_isp,0 ),GATE ("vcp" ,"clk_m" ,29 ,0 , tegra_clk_vcp,0 ), + GATE("ahbdma", "clk_m", 33, 0, tegra_clk_ahbdma, 0),Parent for this should be hclk on Tegra30 and later chips as well..
It looks like other clocks have a wrong parent too here, aren't they? Like for example "apbdma" should have "pclk" as a parent, isn't it?
Quoted 6 lines
Quoted 6 lines
drivers/clk/tegra/clk-tegra-periph.c +0
-0GATE ("apbdma" ,"clk_m" ,34 ,0 , tegra_clk_apbdma,0 ),GATE ("kbc" ,"clk_32k" ,36 ,TEGRA_PERIPH_ON_APB |TEGRA_PERIPH_NO_RESET , tegra_clk_kbc,0 ), GATE("fuse", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse, 0),drivers/clk/tegra/clk-tegra20.c +0
-1--- a/drivers/clk/tegra/clk-tegra20.c
Quoted 9 lines
drivers/clk/tegra/clk-tegra20.c static struct tegra_devclk devclks[] __initdata = { · line 452
Quoted 6 lines
+++
b/drivers/clk/tegra/clk-tegra20.cdrivers/clk/tegra/clk-tegra20.c +1
-0@@ -449,6 +449,7 @@ static struct tegra_devclk devclks[] __initdata = { { .con_id = "audio", .dt_id = TEGRA20_CLK_AUDIO }, { .con_id = "audio_2x", .dt_id = TEGRA20_CLK_AUDIO_2X }, { .dev_id = "tegra20-ac97", .dt_id = TEGRA20_CLK_AC97 }, + { .dev_id = "tegra-ahbdma", .dt_id = TEGRA20_CLK_AHBDMA },This isn't needed if you use DT bindings to get the clock handle.
Yes, I added it for consistency. Shouldn't we get rid of that all legacy stuff already?
Quoted 7 lines
Quoted 7 lines
drivers/clk/tegra/clk-tegra20.c +0
-0{ .dev_id = "tegra-apbdma", .dt_id = TEGRA20_CLK_APBDMA }, { .dev_id = "rtc-tegra", .dt_id = TEGRA20_CLK_RTC }, { .dev_id = "timer", .dt_id = TEGRA20_CLK_TIMER },@@ -806,6 +807,11 @@ static void __init tegra20_periph_clk_init(void) clk_base, 0, 3, periph_clk_enb_refcnt); clks[TEGRA20_CLK_AC97 ] = clk;
Quoted 8 lines
drivers/clk/tegra/clk-tegra20.c static void __init tegra20_periph_clk_init(void) · line 814
Quoted 5 lines
drivers/clk/tegra/clk-tegra20.c +5
-0+/* ahbdma */ + clk =tegra_clk_register_periph_gate ("ahbdma" ,"hclk" ,0 , clk_base, +0 ,33 , periph_clk_enb_refcnt); + clks[TEGRA20_CLK_AHBDMA ] = clk; +You can use the generic definition here if you correct the entry above.
Good point, same applies to "apbdma". Thank you for the suggestion.
Quoted 31 lines
drivers/clk/tegra/clk-tegra30.c static struct tegra_devclk devclks[] __initdata = { · line 615
Quoted 12 lines
drivers/clk/tegra/clk-tegra20.c +0
-0/* apbdma */ clk =tegra_clk_register_periph_gate ("apbdma" ,"pclk" ,0 , clk_base,0 ,34 , periph_clk_enb_refcnt);drivers/clk/tegra/clk-tegra30.c +1
-0@@ -612,6 +612,7 @@ static struct tegra_devclk devclks[] __initdata = { { .con_id = "fuse_burn", .dev_id = "fuse-tegra", .dt_id = TEGRA30_CLK_FUSE_BURN }, { .con_id = "apbif", .dev_id = "tegra30-ahub", .dt_id = TEGRA30_CLK_APBIF }, { .con_id = "hda2hdmi", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA2HDMI }, + { .dev_id = "tegra-ahbdma", .dt_id = TEGRA30_CLK_AHBDMA },Same as for Tegra20.
drivers/clk/tegra/clk-tegra30.c static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = { · line 795
Quoted 11 lines
drivers/clk/tegra/clk-tegra30.c +1
-0{ .dev_id = "tegra-apbdma", .dt_id = TEGRA30_CLK_APBDMA }, { .dev_id = "rtc-tegra", .dt_id = TEGRA30_CLK_RTC }, { .dev_id = "timer", .dt_id = TEGRA30_CLK_TIMER },@@ -788,6 +789,7 @@ static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = { [tegra_clk_extern3] = { .dt_id = TEGRA30_CLK_EXTERN3, .present = true }, [tegra_clk_disp1] = { .dt_id = TEGRA30_CLK_DISP1, .present = true }, [tegra_clk_disp2] = { .dt_id = TEGRA30_CLK_DISP2, .present = true }, + [tegra_clk_ahbdma] = { .dt_id = TEGRA30_CLK_AHBDMA, .present = true }, [tegra_clk_apbdma] = { .dt_id = TEGRA30_CLK_APBDMA, .present = true }, [tegra_clk_rtc] = { .dt_id = TEGRA30_CLK_RTC, .present = true }, [tegra_clk_timer] = { .dt_id = TEGRA30_CLK_TIMER, .present = true },Cheers,
Peter.
Signature · 1 line
Dmitry
Re: [PATCH v1 1/5] clk: tegra: Add AHB DMA clock entry
Quoted 38 lines · Dmitry Osipenko
Quoted 27 lines · Peter De Schrijver
Quoted 26 lines · Dmitry Osipenko
AHB DMA presents on Tegra20/30. Add missing entries, so that driver for AHB DMA could be implemented.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/clk/tegra/clk-id.h | 1 + drivers/clk/tegra/clk-tegra-periph.c | 1 + drivers/clk/tegra/clk-tegra20.c | 6 ++++++ drivers/clk/tegra/clk-tegra30.c | 2 ++ 4 files changed, 10 insertions(+)drivers/clk/tegra/clk-id.h +1
-0@@ -12,6 +12,7 @@ enum clk_id { tegra_clk_amx, tegra_clk_amx1, tegra_clk_apb2ape, + tegra_clk_ahbdma, tegra_clk_apbdma, tegra_clk_apbif, tegra_clk_ape,drivers/clk/tegra/clk-tegra-periph.c +0
-1--- a/drivers/clk/tegra/clk-tegra-periph.cQuoted 9 lines
drivers/clk/tegra/clk-tegra-periph.c static struct tegra_periph_init_data gate_clks[] = { · line 826
Quoted 6 lines
+++
b/drivers/clk/tegra/clk-tegra-periph.cdrivers/clk/tegra/clk-tegra-periph.c +1
-0@@ -823,6 +823,7 @@ static struct tegra_periph_init_data gate_clks[] = {GATE ("timer" ,"clk_m" ,5 ,0 , tegra_clk_timer,CLK_IS_CRITICAL ),GATE ("isp" ,"clk_m" ,23 ,0 , tegra_clk_isp,0 ),GATE ("vcp" ,"clk_m" ,29 ,0 , tegra_clk_vcp,0 ), + GATE("ahbdma", "clk_m", 33, 0, tegra_clk_ahbdma, 0),Parent for this should be hclk on Tegra30 and later chips as well..
Quoted 3 lines
It looks like other clocks have a wrong parent too here, aren't they? Like for example "apbdma" should have "pclk" as a parent, isn't it?
Yes. That is correct.
Quoted 16 lines
Quoted 6 lines
Quoted 6 lines
drivers/clk/tegra/clk-tegra-periph.c +0
-0GATE ("apbdma" ,"clk_m" ,34 ,0 , tegra_clk_apbdma,0 ),GATE ("kbc" ,"clk_32k" ,36 ,TEGRA_PERIPH_ON_APB |TEGRA_PERIPH_NO_RESET , tegra_clk_kbc,0 ), GATE("fuse", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse, 0),drivers/clk/tegra/clk-tegra20.c +0
-1--- a/drivers/clk/tegra/clk-tegra20.cQuoted 9 lines
drivers/clk/tegra/clk-tegra20.c static struct tegra_devclk devclks[] __initdata = { · line 452
Quoted 6 lines
+++
b/drivers/clk/tegra/clk-tegra20.cdrivers/clk/tegra/clk-tegra20.c +1
-0@@ -449,6 +449,7 @@ static struct tegra_devclk devclks[] __initdata = { { .con_id = "audio", .dt_id = TEGRA20_CLK_AUDIO }, { .con_id = "audio_2x", .dt_id = TEGRA20_CLK_AUDIO_2X }, { .dev_id = "tegra20-ac97", .dt_id = TEGRA20_CLK_AC97 }, + { .dev_id = "tegra-ahbdma", .dt_id = TEGRA20_CLK_AHBDMA },This isn't needed if you use DT bindings to get the clock handle.
Quoted 3 lines
Yes, I added it for consistency. Shouldn't we get rid of that all legacy stuff already?
We probably should, but we can start by not adding more :)
Quoted 16 lines
Quoted 7 lines
Quoted 7 lines
drivers/clk/tegra/clk-tegra20.c +0
-0{ .dev_id = "tegra-apbdma", .dt_id = TEGRA20_CLK_APBDMA }, { .dev_id = "rtc-tegra", .dt_id = TEGRA20_CLK_RTC }, { .dev_id = "timer", .dt_id = TEGRA20_CLK_TIMER },@@ -806,6 +807,11 @@ static void __init tegra20_periph_clk_init(void) clk_base, 0, 3, periph_clk_enb_refcnt); clks[TEGRA20_CLK_AC97 ] = clk;Quoted 8 lines
drivers/clk/tegra/clk-tegra20.c static void __init tegra20_periph_clk_init(void) · line 814
Quoted 5 lines
drivers/clk/tegra/clk-tegra20.c +5
-0+/* ahbdma */ + clk =tegra_clk_register_periph_gate ("ahbdma" ,"hclk" ,0 , clk_base, +0 ,33 , periph_clk_enb_refcnt); + clks[TEGRA20_CLK_AHBDMA ] = clk; +You can use the generic definition here if you correct the entry above.
Quoted 2 lines
Good point, same applies to "apbdma". Thank you for the suggestion.
Indeed.
Peter.
Re: [PATCH v1 1/5] clk: tegra: Add AHB DMA clock entry
Quoted 39 lines · Peter De Schrijver
Quoted 38 lines · Dmitry Osipenko
Quoted 36 lines · Peter De Schrijver
Quoted 28 lines · Dmitry Osipenko
AHB DMA presents on Tegra20/30. Add missing entries, so that driver for AHB DMA could be implemented.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/clk/tegra/clk-id.h | 1 + drivers/clk/tegra/clk-tegra-periph.c | 1 + drivers/clk/tegra/clk-tegra20.c | 6 ++++++ drivers/clk/tegra/clk-tegra30.c | 2 ++ 4 files changed, 10 insertions(+)drivers/clk/tegra/clk-id.h +1
-0@@ -12,6 +12,7 @@ enum clk_id { tegra_clk_amx, tegra_clk_amx1, tegra_clk_apb2ape, + tegra_clk_ahbdma, tegra_clk_apbdma, tegra_clk_apbif, tegra_clk_ape,drivers/clk/tegra/clk-tegra-periph.c +0
-0drivers/clk/tegra/clk-tegra-periph.c static struct tegra_periph_init_data gate_clks[] = { · line 826
Quoted 4 lines
drivers/clk/tegra/clk-tegra-periph.c +1
-0@@ -823,6 +823,7 @@ static struct tegra_periph_init_data gate_clks[] = {GATE ("timer" ,"clk_m" ,5 ,0 , tegra_clk_timer,CLK_IS_CRITICAL ),GATE ("isp" ,"clk_m" ,23 ,0 , tegra_clk_isp,0 ),GATE ("vcp" ,"clk_m" ,29 ,0 , tegra_clk_vcp,0 ), + GATE("ahbdma", "clk_m", 33, 0, tegra_clk_ahbdma, 0),Parent for this should be hclk on Tegra30 and later chips as well..
Quoted 6 lines
Quoted 3 lines
It looks like other clocks have a wrong parent too here, aren't they? Like for example "apbdma" should have "pclk" as a parent, isn't it?
Yes. That is correct.
Okay, I'll fix it in V2.
Quoted 16 lines
Quoted 16 lines
Quoted 15 lines
drivers/clk/tegra/clk-tegra20.c static struct tegra_devclk devclks[] __initdata = { · line 452
Quoted 12 lines
drivers/clk/tegra/clk-tegra-periph.c +0
-0GATE ("apbdma" ,"clk_m" ,34 ,0 , tegra_clk_apbdma,0 ),GATE ("kbc" ,"clk_32k" ,36 ,TEGRA_PERIPH_ON_APB |TEGRA_PERIPH_NO_RESET , tegra_clk_kbc,0 ), GATE("fuse", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse, 0),drivers/clk/tegra/clk-tegra20.c +1
-0@@ -449,6 +449,7 @@ static struct tegra_devclk devclks[] __initdata = { { .con_id = "audio", .dt_id = TEGRA20_CLK_AUDIO }, { .con_id = "audio_2x", .dt_id = TEGRA20_CLK_AUDIO_2X }, { .dev_id = "tegra20-ac97", .dt_id = TEGRA20_CLK_AC97 }, + { .dev_id = "tegra-ahbdma", .dt_id = TEGRA20_CLK_AHBDMA },This isn't needed if you use DT bindings to get the clock handle.
Quoted 6 lines
Quoted 3 lines
Yes, I added it for consistency. Shouldn't we get rid of that all legacy stuff already?
We probably should, but we can start by not adding more :)
Sure ;)
Quoted 20 lines
Quoted 16 lines
Quoted 7 lines
Quoted 7 lines
drivers/clk/tegra/clk-tegra20.c +0
-0{ .dev_id = "tegra-apbdma", .dt_id = TEGRA20_CLK_APBDMA }, { .dev_id = "rtc-tegra", .dt_id = TEGRA20_CLK_RTC }, { .dev_id = "timer", .dt_id = TEGRA20_CLK_TIMER },@@ -806,6 +807,11 @@ static void __init tegra20_periph_clk_init(void) clk_base, 0, 3, periph_clk_enb_refcnt); clks[TEGRA20_CLK_AC97 ] = clk;Quoted 8 lines
drivers/clk/tegra/clk-tegra20.c static void __init tegra20_periph_clk_init(void) · line 814
Quoted 5 lines
drivers/clk/tegra/clk-tegra20.c +5
-0+/* ahbdma */ + clk =tegra_clk_register_periph_gate ("ahbdma" ,"hclk" ,0 , clk_base, +0 ,33 , periph_clk_enb_refcnt); + clks[TEGRA20_CLK_AHBDMA ] = clk; +You can use the generic definition here if you correct the entry above.
Quoted 2 lines
Good point, same applies to "apbdma". Thank you for the suggestion.
Indeed.
Signature · 1 line
Dmitry
PATCH 2/5 clk: tegra: Bump SCLK clock rate to 216MHz on Tegra20
In reply to Dmitry OsipenkoAHB DMA is a running on 1/2 of SCLK rate, APB on 1/4. Increasing SCLK rate results in an increased DMA transfer rate.
--- drivers/clk/tegra/clk-tegra20.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/clk/tegra/clk-tegra20.c +1 -1
@@ -1031,7 +1031,7 @@ static struct tegra_clk_init_table init_table[] __initdata = { { TEGRA20_CLK_PLL_P_OUT3, TEGRA20_CLK_CLK_MAX, 72000000, 1 }, { TEGRA20_CLK_PLL_P_OUT4, TEGRA20_CLK_CLK_MAX, 24000000, 1 }, { TEGRA20_CLK_PLL_C, TEGRA20_CLK_CLK_MAX, 600000000, 1 }, - { TEGRA20_CLK_PLL_C_OUT1, TEGRA20_CLK_CLK_MAX, 120000000, 1 }, + { TEGRA20_CLK_PLL_C_OUT1, TEGRA20_CLK_CLK_MAX, 216000000, 1 }, { TEGRA20_CLK_SCLK, TEGRA20_CLK_PLL_C_OUT1, 0, 1 }, { TEGRA20_CLK_HCLK, TEGRA20_CLK_CLK_MAX, 0, 1 }, { TEGRA20_CLK_PCLK, TEGRA20_CLK_CLK_MAX, 60000000, 1 },
Signature · 1 line
2.14.1
Re: [PATCH v1 2/5] clk: tegra: Bump SCLK clock rate to 216MHz on Tegra20
Quoted 4 lines · Dmitry Osipenko
AHB DMA is a running on 1/2 of SCLK rate, APB on 1/4. Increasing SCLK rate results in an increased DMA transfer rate.
Signed-off-by Dmitry Osipenko <[email protected]>
Quoted 20 lines
--- drivers/clk/tegra/clk-tegra20.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/clk/tegra/clk-tegra20.c +1
-1@@ -1031,7 +1031,7 @@ static struct tegra_clk_init_table init_table[] __initdata = { { TEGRA20_CLK_PLL_P_OUT3, TEGRA20_CLK_CLK_MAX, 72000000, 1 }, { TEGRA20_CLK_PLL_P_OUT4, TEGRA20_CLK_CLK_MAX, 24000000, 1 }, { TEGRA20_CLK_PLL_C, TEGRA20_CLK_CLK_MAX, 600000000, 1 }, - { TEGRA20_CLK_PLL_C_OUT1, TEGRA20_CLK_CLK_MAX, 120000000, 1 }, + { TEGRA20_CLK_PLL_C_OUT1, TEGRA20_CLK_CLK_MAX, 216000000, 1 }, { TEGRA20_CLK_SCLK, TEGRA20_CLK_PLL_C_OUT1, 0, 1 }, { TEGRA20_CLK_HCLK, TEGRA20_CLK_CLK_MAX, 0, 1 }, { TEGRA20_CLK_PCLK, TEGRA20_CLK_CLK_MAX, 60000000, 1 },Signature · 1 line
2.14.1
PATCH 3/5 dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
In reply to Dmitry OsipenkoDocument DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +23 -0
@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt. +- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field. + +Example: + +ahbdma: ahbdma@60008000 { + compatible = "nvidia,tegra20-ahbdma"; + reg = <0x60008000 0x2000>; + interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&tegra_car TEGRA20_CLK_AHBDMA>; + resets = <&tegra_car 33>; + #dma-cells = <1>; +};
Signature · 1 line
2.14.1
Jon Hunter
maintainerRe: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 17 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtDocumentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +2
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller +
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 12
Quoted 10 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +10
-0+Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt. +- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field.
What about the TRIG_SEL field? Do we need to handle this here as well?
Cheers
Jon
Signature · 1 line
nvpublic
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 23 lines · Jon Hunter
Quoted 21 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtDocumentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt.
Quoted 9 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 12
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0+- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field.What about the
TRIG_SELfield? Do we need to handle this here as well?
I've followed APB DMA here, that HW also has TRIG_SEL but ignores it for some reason. I think technically it should be present in the binding, yeah.
Signature · 1 line
Dmitry
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
In reply to Jon HunterQuoted 23 lines · Jon Hunter
Quoted 21 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtDocumentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt.
Quoted 9 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 12
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0+- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field.What about the
TRIG_SELfield? Do we need to handle this here as well?
Actually, DMA transfer trigger isn't related a hardware description. It's up to software to decide what trigger to select. So it shouldn't be in the binding.
And I think the same applies to requester... any objections?
Signature · 1 line
Dmitry
Jon Hunter
maintainerRe: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 34 lines · Dmitry Osipenko
Quoted 23 lines · Jon Hunter
Quoted 21 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtDocumentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt.Quoted 9 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 12
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0+- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field.What about the
TRIG_SELfield? Do we need to handle this here as well?
Quoted 2 lines
Actually, DMA transfer trigger isn't related a hardware description. It's up to software to decide what trigger to select. So it shouldn't be in the binding.
I think it could be, if say a board wanted a GPIO to trigger a transfer.
Quoted 1 line
And I think the same applies to requester... any objections?
Well, the REQ_SEL should definitely be in the binding.
Laxman, Stephen, what are your thoughts on the TRIG_SEL field? Looks like we never bothered with it for the APB DMA and so maybe no ones uses this.
Cheers
Jon
Signature · 1 line
nvpublic
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 36 lines · Jon Hunter
Quoted 34 lines · Dmitry Osipenko
Quoted 32 lines · Jon Hunter
Quoted 17 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtDocumentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +2
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller +Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 12
Quoted 10 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +10
-0+Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt. +- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field.What about the
TRIG_SELfield? Do we need to handle this here as well?
Quoted 5 lines
Quoted 2 lines
Actually, DMA transfer trigger isn't related a hardware description. It's up to software to decide what trigger to select. So it shouldn't be in the binding.
I think it could be, if say a board wanted a GPIO to trigger a transfer.
GPIO isn't a very good example, there is no "GPIO" trigger. To me all triggers are software-defined, so that software could create transfer chains.
Quoted 4 lines
Quoted 1 line
And I think the same applies to requester... any objections?
Well, the
REQ_SELshould definitely be in the binding.
Okay.
Quoted 4 lines
Laxman, Stephen, what are your thoughts on the
TRIG_SELfield? Looks like we never bothered with it for the APB DMA and so maybe no ones uses this.
Signature · 1 line
Dmitry
Jon Hunter
maintainerRe: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 37 lines · Dmitry Osipenko
Quoted 36 lines · Jon Hunter
Quoted 34 lines · Dmitry Osipenko
Quoted 32 lines · Jon Hunter
Quoted 17 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtDocumentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +2
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller +Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 12
Quoted 10 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +10
-0+Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt. +- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field.What about the
TRIG_SELfield? Do we need to handle this here as well?
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
Actually, DMA transfer trigger isn't related a hardware description. It's up to software to decide what trigger to select. So it shouldn't be in the binding.
I think it could be, if say a board wanted a GPIO to trigger a transfer.
GPIO isn't a very good example, there is no "GPIO" trigger. To me all triggers are software-defined, so that software could create transfer chains.
TRM shows the following in the APBDMA_TRIG_REG_0 ...
"XRQ_A: XRQ.A (GPIOA) (Hardware initiated DMA request)"
Jon
Signature · 1 line
nvpublic
Jon Hunter
maintainerRe: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 45 lines · Jon Hunter
Quoted 43 lines · Dmitry Osipenko
Quoted 41 lines · Jon Hunter
Quoted 34 lines · Dmitry Osipenko
Quoted 23 lines · Jon Hunter
Quoted 21 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtDocumentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt.Quoted 9 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 12
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0+- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field.What about the
TRIG_SELfield? Do we need to handle this here as well?Quoted 2 lines
Actually, DMA transfer trigger isn't related a hardware description. It's up to software to decide what trigger to select. So it shouldn't be in the binding.
I think it could be, if say a board wanted a GPIO to trigger a transfer.
Quoted 6 lines
Quoted 2 lines
GPIO isn't a very good example, there is no "GPIO" trigger. To me all triggers are software-defined, so that software could create transfer chains.
TRM shows the following in the
APBDMA_TRIG_REG_0..."
XRQ_A: XRQ.A (GPIOA) (Hardware initiated DMA request)"
Furthermore there are timer and hw-semaphore triggers as well.
Jon
Signature · 1 line
nvpublic
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 47 lines · Jon Hunter
Quoted 45 lines · Jon Hunter
Quoted 43 lines · Dmitry Osipenko
Quoted 41 lines · Jon Hunter
Quoted 34 lines · Dmitry Osipenko
Quoted 23 lines · Jon Hunter
Quoted 21 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtDocumentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt.Quoted 9 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 12
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0+- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field.What about the
TRIG_SELfield? Do we need to handle this here as well?Quoted 2 lines
Actually, DMA transfer trigger isn't related a hardware description. It's up to software to decide what trigger to select. So it shouldn't be in the binding.
I think it could be, if say a board wanted a GPIO to trigger a transfer.
Quoted 9 lines
Quoted 6 lines
Quoted 2 lines
GPIO isn't a very good example, there is no "GPIO" trigger. To me all triggers are software-defined, so that software could create transfer chains.
TRM shows the following in the
APBDMA_TRIG_REG_0..."
XRQ_A: XRQ.A (GPIOA) (Hardware initiated DMA request)"Furthermore there are timer and hw-semaphore triggers as well.
Aha, I wasn't sure about what XRQ is. AHB DMA doesn't have XRQ.A as a trigger, but XRQ.C/D which I suppose corresponds to GPIO C/D.
Timer and hw-semaphore are more questionable, aren't semaphores software-only triggerable?
Signature · 1 line
Dmitry
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
In reply to Jon HunterQuoted 5 lines · Jon Hunter
Well, the
REQ_SELshould definitely be in the binding.Laxman, Stephen, what are your thoughts on the
TRIG_SELfield? Looks
Wrong Stephen?
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Jon Hunter
maintainerRe: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 8 lines · Stephen Boyd
Quoted 5 lines · Jon Hunter
Well, the
REQ_SELshould definitely be in the binding.Laxman, Stephen, what are your thoughts on the
TRIG_SELfield? LooksWrong Stephen?
Indeed! With all the folks in copy, I assumed we had the right one :-)
Cheers
Jon
Signature · 1 line
nvpublic
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
In reply to Jon HunterQuoted 41 lines · Jon Hunter
Quoted 34 lines · Dmitry Osipenko
Quoted 23 lines · Jon Hunter
Quoted 21 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+)create mode 100644
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtDocumentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt.Quoted 9 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 12
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0+- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field.What about the
TRIG_SELfield? Do we need to handle this here as well?Quoted 2 lines
Actually, DMA transfer trigger isn't related a hardware description. It's up to software to decide what trigger to select. So it shouldn't be in the binding.
I think it could be, if say a board wanted a GPIO to trigger a transfer.
Quoted 7 lines
Quoted 1 line
And I think the same applies to requester... any objections?
Well, the
REQ_SELshould definitely be in the binding.Laxman, Stephen, what are your thoughts on the
TRIG_SELfield? Looks like we never bothered with it for the APB DMA and so maybe no ones uses this.
I don't think TRIG_SEL should be in the binding, at least at present. While TRIG_SEL certainly is something used to configure the transfer, I believe the semantics of the current DMA binding only cover DMA transfers that are initiated when SW desires, rather than being a combination of after SW programs the transfer plus some other HW event. So, we always use a default/hard-coded TRIG_SEL value. As such, there's no need for a TRIG_SEL value in DT. There's certainly no known use-case that requires a non-default TRIG_SEL value at present. We could add an extra #dma-cells value later if we find a use for it, and the semantics of that use-case make sense to add it to the DMA specifier, rather than some other separate higher-level property/driver/...
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 54 lines · Stephen Warren
Quoted 45 lines · Jon Hunter
Quoted 38 lines · Dmitry Osipenko
Quoted 27 lines · Jon Hunter
Quoted 25 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>---
.../bindings/dma/nvidia,tegra20-ahbdma.txt | 23
++++++++++++++++++++++1 file changed, 23 insertions(+)create mode 100644
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtdiff --git
a/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtb/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtnew file mode 100644
index 000000000000..2af9aa76ae11Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt.Quoted 9 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 12
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0+- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field.What about the
TRIG_SELfield? Do we need to handle this here as well?Quoted 2 lines
Actually, DMA transfer trigger isn't related a hardware description. It's up to software to decide what trigger to select. So it shouldn't be in the binding.
I think it could be, if say a board wanted a GPIO to trigger a transfer.
Quoted 7 lines
Quoted 1 line
And I think the same applies to requester... any objections?
Well, the
REQ_SELshould definitely be in the binding.Laxman, Stephen, what are your thoughts on the
TRIG_SELfield? Looks like we never bothered with it for the APB DMA and so maybe no ones uses this.
Quoted 10 lines
I don't think
TRIG_SELshould be in the binding, at least at present. WhileTRIG_SELcertainly is something used to configure the transfer, I believe the semantics of the current DMA binding only cover DMA transfers that are initiated when SW desires, rather than being a combination of after SW programs the transfer plus some other HW event. So, we always use a default/hard-codedTRIG_SELvalue. As such, there's no need for aTRIG_SELvalue in DT. There's certainly no known use-case that requires a non-defaultTRIG_SELvalue at present. We could add an extra #dma-cells value later if we find a use for it, and the semantics of that use-case make sense to add it to the DMA specifier, rather than some other separate higher-level property/driver/...
Thank you for the comment. If we'd want to extend the binding further with the trigger, how to differentiate trigger from the requester in a case of a single #data-cell?
Of course realistically a chance that the further extension would be needed is very-very low, so we may defer the efforts to solve that question and for now make driver aware of the potential #dma-cells extension.
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 66 lines · Dmitry Osipenko
Quoted 54 lines · Stephen Warren
Quoted 45 lines · Jon Hunter
Quoted 38 lines · Dmitry Osipenko
Quoted 27 lines · Jon Hunter
Quoted 25 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+)create mode 100644
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtdiff --git
a/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtb/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtnew file mode 100644
index 000000000000..2af9aa76ae11Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt.Quoted 9 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 12
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0+- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field.What about the
TRIG_SELfield? Do we need to handle this here as well?Quoted 2 lines
Actually, DMA transfer trigger isn't related a hardware description. It's up to software to decide what trigger to select. So it shouldn't be in the binding.
I think it could be, if say a board wanted a GPIO to trigger a transfer.
Quoted 7 lines
Quoted 1 line
And I think the same applies to requester... any objections?
Well, the
REQ_SELshould definitely be in the binding.Laxman, Stephen, what are your thoughts on the
TRIG_SELfield? Looks like we never bothered with it for the APB DMA and so maybe no ones uses this.Quoted 10 lines
I don't think
TRIG_SELshould be in the binding, at least at present. WhileTRIG_SELcertainly is something used to configure the transfer, I believe the semantics of the current DMA binding only cover DMA transfers that are initiated when SW desires, rather than being a combination of after SW programs the transfer plus some other HW event. So, we always use a default/hard-codedTRIG_SELvalue. As such, there's no need for aTRIG_SELvalue in DT. There's certainly no known use-case that requires a non-defaultTRIG_SELvalue at present. We could add an extra #dma-cells value later if we find a use for it, and the semantics of that use-case make sense to add it to the DMA specifier, rather than some other separate higher-level property/driver/...
Quoted 7 lines
Thank you for the comment. If we'd want to extend the binding further with the trigger, how to differentiate trigger from the requester in a case of a single #data-cell?
Of course realistically a chance that the further extension would be needed is very-very low, so we may defer the efforts to solve that question and for now make driver aware of the potential #dma-cells extension.
The request selector cell isn't optional, so is always present. If we later add an optional trig_sel cell, we'll either have:
#dma-cells=<1>: req_sel
or:
#dma-cells=<2>: req_sel, trig_sel
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 77 lines · Stephen Warren
Quoted 68 lines · Dmitry Osipenko
Quoted 56 lines · Stephen Warren
Quoted 47 lines · Jon Hunter
Quoted 39 lines · Dmitry Osipenko
Quoted 28 lines · Jon Hunter
Quoted 26 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>---
.../bindings/dma/nvidia,tegra20-ahbdma.txt | 23
++++++++++++++++++++++1 file changed, 23 insertions(+)create mode 100644
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtdiff --git
a/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtb/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtnew file mode 100644
index 000000000000..2af9aa76ae11Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +7
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt. +- clocks: Should contain one entry, DMA controller clock.Quoted 9 lines
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +2
-0+- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request selectvalue
+ for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register +REQ_SELfield.What about the
TRIG_SELfield? Do we need to handle this here as well?Quoted 3 lines
Actually, DMA transfer trigger isn't related a hardware description. It's up to
software to decide what trigger to select. So it shouldn't be in the binding.I think it could be, if say a board wanted a GPIO to trigger a transfer.
Quoted 7 lines
Quoted 1 line
And I think the same applies to requester... any objections?
Well, the
REQ_SELshould definitely be in the binding.Laxman, Stephen, what are your thoughts on the
TRIG_SELfield? Looks like we never bothered with it for the APB DMA and so maybe no ones uses this.Quoted 10 lines
I don't think
TRIG_SELshould be in the binding, at least at present. WhileTRIG_SELcertainly is something used to configure the transfer, I believe the semantics of the current DMA binding only cover DMA transfers that are initiated when SW desires, rather than being a combination of after SW programs the transfer plus some other HW event. So, we always use a default/hard-codedTRIG_SELvalue. As such, there's no need for aTRIG_SELvalue in DT. There's certainly no known use-case that requires a non-defaultTRIG_SELvalue at present. We could add an extra #dma-cells value later if we find a use for it, and the semantics of that use-case make sense to add it to the DMA specifier, rather than some other separate higher-level property/driver/...Quoted 7 lines
Thank you for the comment. If we'd want to extend the binding further with the trigger, how to differentiate trigger from the requester in a case of a single #data-cell?
Of course realistically a chance that the further extension would be needed is very-very low, so we may defer the efforts to solve that question and for now make driver aware of the potential #dma-cells extension.
Quoted 8 lines
The request selector cell isn't optional, so is always present. If we later add an optional
trig_selcell, we'll either have:#dma-cells=<1>:
req_selor:
#dma-cells=<2>:
req_sel,trig_sel
Why request sel. couldn't be optional? Could you please elaborate a bit more?
I think possible options are:
#dma-cells=<1>: req_sel
#dma-cells=<1>: trig_sel
#dma-cells=<2>: req_sel, trig_sel
The only difference between request and trigger is that trigger issues the whole transfer, while request only a single burst. Isn't it possible to have a case in HW for the "trigger-only" option? If not or it's a rareness, then I agree that REQ_SEL must be mandatory.
Jon Hunter
maintainerRe: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 10 lines · Dmitry Osipenko
Quoted 9 lines · Stephen Warren
Quoted 8 lines · Dmitry Osipenko
Quoted 7 lines · Stephen Warren
Quoted 6 lines · Jon Hunter
Quoted 4 lines · Dmitry Osipenko
Quoted 3 lines · Jon Hunter
Quoted 1 line · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that pres=
ents
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>---
=C2=A0=C2=A0 .../bindings/dma/nvidia,tegra20-ahbdma.txt=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 | 23
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
++++++++++++++++++++++
=C2=A0=C2=A0 1 file changed, 23 insertions(+) =C2=A0=C2=A0 create mode 100644Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.=
Quoted 7 lines
diff --git
a/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtb/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtnew file mode 100644
index 000000000000..2af9aa76ae11Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.= new file +0
-0
txt
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.= line 5
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.= new file +5
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible:=C2=A0=C2=A0=C2=A0 Must be "nvidia,tegra20-ahbdma" +- reg:=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Should contain r=
egisters base address and length.
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.= line 6
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.= new file +1
-0+- interrupts:=C2=A0=C2=A0=C2=A0 Should contain one entry, DMA con=
troller interrupt.
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.= line 7
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.= new file +1
-0+- clocks:=C2=A0=C2=A0=C2=A0 Should contain one entry, DMA control=
ler clock.
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.= line 8
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.= new file +1
-0+- resets :=C2=A0=C2=A0=C2=A0 Should contain one entry, DMA contro=
ller reset.
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.= line 9
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.= new file +1
-0+- #dma-cells:=C2=A0=C2=A0=C2=A0 Should be <1>. The cell represent=
s DMA request select
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
value
+=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 for the peripheral. Fo=
r more details consult the Tegra TRM's
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
+=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 documentation, in part=
icular AHB DMA channel control register
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
+=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0
REQ_SELfield.What about the
TRIG_SELfield? Do we need to handle this here as we=
ll?
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
Actually, DMA transfer trigger isn't related a hardware description.=
It's
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
up to
software to decide what trigger to select. So it shouldn't be in the=
binding.
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
I think it could be, if say a board wanted a GPIO to trigger a transf=
er.
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 2 lines
And I think the same applies to requester... any objections?
Well, the
REQ_SELshould definitely be in the binding.Laxman, Stephen, what are your thoughts on the
TRIG_SELfield? Looks like we never bothered with it for the APB DMA and so maybe no ones u=
ses
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
this.
I don't think
TRIG_SELshould be in the binding, at least at present. =
While
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
TRIG_SELcertainly is something used to configure the transfer, I beli=
eve the
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
semantics of the current DMA binding only cover DMA transfers that are=
initiated
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
when SW desires, rather than being a combination of after SW programs =
the
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
transfer plus some other HW event. So, we always use a default/hard-co=
ded
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
TRIG_SELvalue. As such, there's no need for aTRIG_SELvalue in DT. T=
here's
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
certainly no known use-case that requires a non-default
TRIG_SELvalue=
at
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
present. We could add an extra #dma-cells value later if we find a use=
for it,
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
and the semantics of that use-case make sense to add it to the DMA spe=
cifier,
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
rather than some other separate higher-level property/driver/...
Thank you for the comment. If we'd want to extend the binding further w=
ith the
Quoted 1 line
Quoted 1 line
Quoted 1 line
trigger, how to differentiate trigger from the requester in a case of a=
single
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
#data-cell?
Of course realistically a chance that the further extension would be ne=
eded is
Quoted 1 line
Quoted 1 line
Quoted 1 line
very-very low, so we may defer the efforts to solve that question and f=
or now
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
make driver aware of the potential #dma-cells extension.
The request selector cell isn't optional, so is always present. If we la=
ter add
Quoted 9 lines
Quoted 7 lines
an optional
trig_selcell, we'll either have:#dma-cells=3D<1>:
req_selor:
#dma-cells=3D<2>:
req_sel,trig_sel=20
Why request sel. couldn't be optional? Could you please elaborate a bit m=
ore?
Quoted 5 lines
=20
I think possible options are:
=20
#dma-cells=3D<1>:req_sel
#dma-cells=3D<1>:trig_sel
With the above, how would you know that it is the req_sel or trig_sel that is specified?
Quoted 3 lines
#dma-cells=3D<2>:
req_sel,trig_sel
=20
The only difference between request and trigger is that trigger issues th=
e whole
Quoted 1 line
transfer, while request only a single burst. Isn't it possible to have a =
case in
Quoted 1 line
HW for the "trigger-only" option? If not or it's a rareness, then I agree=
that
Quoted 1 line
REQ_SELmust be mandatory.
I think that what Stephen is proposing is that for now we go with '#dma-cells=3D<1>' and if we ever need to support the trigger cell we could add support for '#dma-cells=3D<2>'. So with this proposal the 'req_sel' would always be required for both '#dma-cells=3D<1>' and '#dma-cells=3D<2>'. Even if the req_sel is not actually used but the 'trig_sel' is, the user would have to set 'req_sel' to some pre-defined value (eg. -1) where we know to ignore it.
Cheers
Jon
--=20
nvpublic
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 107 lines · Jon Hunter
Quoted 85 lines · Dmitry Osipenko
Quoted 84 lines · Stephen Warren
Quoted 68 lines · Dmitry Osipenko
Quoted 56 lines · Stephen Warren
Quoted 47 lines · Jon Hunter
Quoted 39 lines · Dmitry Osipenko
Quoted 28 lines · Jon Hunter
Quoted 26 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>---
.../bindings/dma/nvidia,tegra20-ahbdma.txt | 23
++++++++++++++++++++++1 file changed, 23 insertions(+)create mode 100644
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtdiff --git
a/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtb/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtnew file mode 100644
index 000000000000..2af9aa76ae11Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +7
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt. +- clocks: Should contain one entry, DMA controller clock.Quoted 9 lines
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +2
-0+- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request selectvalue
+ for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register +REQ_SELfield.What about the
TRIG_SELfield? Do we need to handle this here as well?Quoted 3 lines
Actually, DMA transfer trigger isn't related a hardware description. It's up to
software to decide what trigger to select. So it shouldn't be in the binding.I think it could be, if say a board wanted a GPIO to trigger a transfer.
Quoted 7 lines
Quoted 1 line
And I think the same applies to requester... any objections?
Well, the
REQ_SELshould definitely be in the binding.Laxman, Stephen, what are your thoughts on the
TRIG_SELfield? Looks like we never bothered with it for the APB DMA and so maybe no ones uses this.Quoted 10 lines
I don't think
TRIG_SELshould be in the binding, at least at present. WhileTRIG_SELcertainly is something used to configure the transfer, I believe the semantics of the current DMA binding only cover DMA transfers that are initiated when SW desires, rather than being a combination of after SW programs the transfer plus some other HW event. So, we always use a default/hard-codedTRIG_SELvalue. As such, there's no need for aTRIG_SELvalue in DT. There's certainly no known use-case that requires a non-defaultTRIG_SELvalue at present. We could add an extra #dma-cells value later if we find a use for it, and the semantics of that use-case make sense to add it to the DMA specifier, rather than some other separate higher-level property/driver/...Quoted 7 lines
Thank you for the comment. If we'd want to extend the binding further with the trigger, how to differentiate trigger from the requester in a case of a single #data-cell?
Of course realistically a chance that the further extension would be needed is very-very low, so we may defer the efforts to solve that question and for now make driver aware of the potential #dma-cells extension.
The request selector cell isn't optional, so is always present. If we later add an optional
trig_selcell, we'll either have:#dma-cells=<1>:
req_selor:
Quoted 8 lines
Quoted 1 line
#dma-cells=<2>:
req_sel,trig_selWhy request sel. couldn't be optional? Could you please elaborate a bit more?
I think possible options are:
#dma-cells=<1>:
req_sel
#dma-cells=<1>:trig_selWith the above, how would you know that it is the
req_selortrig_selthat is specified?Quoted 6 lines
#dma-cells=<2>:
req_sel,trig_selThe only difference between request and trigger is that trigger issues the whole transfer, while request only a single burst. Isn't it possible to have a case in HW for the "trigger-only" option? If not or it's a rareness, then I agree that
REQ_SELmust be mandatory.
Quoted 8 lines
I think that what Stephen is proposing is that for now we go with '#dma-cells=<1>' and if we ever need to support the trigger cell we could add support for '#dma-cells=<2>'. So with this proposal the '
req_sel' would always be required for both '#dma-cells=<1>' and '#dma-cells=<2>'. Even if thereq_selis not actually used but the 'trig_sel' is, the user would have to set 'req_sel' to some pre-defined value (eg. -1) where we know to ignore it.
Okay, I see now. Thank you for the clarification, but then we should have that pre-defined value declared in the binding?
Jon Hunter
maintainerRe: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 6 lines · Dmitry Osipenko
Quoted 5 lines · Jon Hunter
Quoted 2 lines · Dmitry Osipenko
Quoted 1 line · Stephen Warren
On 09/29/2017 09:11 PM, Dmitry Osipenko wrote:
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines · Stephen Warren
Quoted 7 lines · Jon Hunter
Quoted 6 lines · Dmitry Osipenko
Quoted 4 lines · Jon Hunter
Quoted 3 lines · Dmitry Osipenko
Quoted 1 line
Document DT bindings for NVIDIA Tegra AHB DMA controller that pr=
esents
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>---
=C2=A0=C2=A0 .../bindings/dma/nvidia,tegra20-ahbdma.txt=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 | 23
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
++++++++++++++++++++++
=C2=A0=C2=A0 1 file changed, 23 insertions(+) =C2=A0=C2=A0 create mode 100644Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtdiff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.tx=
t
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
b/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.tx=
t
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdm=
Quoted 4 lines
new file mode 100644
index 000000000000..2af9aa76ae11Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdm= new file +0
-0
a.txt
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdm= line 5
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdm= new file +5
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible:=C2=A0=C2=A0=C2=A0 Must be "nvidia,tegra20-ahbdma" +- reg:=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Should contain=
registers base address and length.
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdm= line 6
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdm= new file +1
-0+- interrupts:=C2=A0=C2=A0=C2=A0 Should contain one entry, DMA c=
ontroller interrupt.
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdm= line 7
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdm= new file +1
-0+- clocks:=C2=A0=C2=A0=C2=A0 Should contain one entry, DMA contr=
oller clock.
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdm= line 8
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdm= new file +1
-0+- resets :=C2=A0=C2=A0=C2=A0 Should contain one entry, DMA cont=
roller reset.
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdm= line 9
Quoted 1 line
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdm= new file +1
-0+- #dma-cells:=C2=A0=C2=A0=C2=A0 Should be <1>. The cell represe=
nts DMA request select
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
value
+=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 for the peripheral. =
For more details consult the Tegra TRM's
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
+=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 documentation, in pa=
rticular AHB DMA channel control register
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
+=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0
REQ_SELfield.What about the
TRIG_SELfield? Do we need to handle this here as =
well?
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
Actually, DMA transfer trigger isn't related a hardware descriptio=
n. It's
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
up to
software to decide what trigger to select. So it shouldn't be in t=
he binding.
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
I think it could be, if say a board wanted a GPIO to trigger a tran=
sfer.
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 2 lines
And I think the same applies to requester... any objections?
Well, the
REQ_SELshould definitely be in the binding.Laxman, Stephen, what are your thoughts on the
TRIG_SELfield? Look=
s
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
like we never bothered with it for the APB DMA and so maybe no ones=
uses
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
this.
I don't think
TRIG_SELshould be in the binding, at least at present=
. While
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
TRIG_SELcertainly is something used to configure the transfer, I be=
lieve the
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
semantics of the current DMA binding only cover DMA transfers that a=
re initiated
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
when SW desires, rather than being a combination of after SW program=
s the
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
transfer plus some other HW event. So, we always use a default/hard-=
coded
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
TRIG_SELvalue. As such, there's no need for aTRIG_SELvalue in DT.=
There's
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
certainly no known use-case that requires a non-default
TRIG_SELval=
ue at
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
present. We could add an extra #dma-cells value later if we find a u=
se for it,
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
and the semantics of that use-case make sense to add it to the DMA s=
pecifier,
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
rather than some other separate higher-level property/driver/...
Thank you for the comment. If we'd want to extend the binding further=
with the
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
trigger, how to differentiate trigger from the requester in a case of=
a single
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
#data-cell?
Of course realistically a chance that the further extension would be =
needed is
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
very-very low, so we may defer the efforts to solve that question and=
for now
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line
make driver aware of the potential #dma-cells extension.
The request selector cell isn't optional, so is always present. If we =
later add
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 7 lines
an optional
trig_selcell, we'll either have:#dma-cells=3D<1>:
req_selor:
#dma-cells=3D<2>:
req_sel,trig_selWhy request sel. couldn't be optional? Could you please elaborate a bit=
more?
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
I think possible options are:
#dma-cells=3D<1>:
req_sel
#dma-cells=3D<1>:trig_sel
Quoted 6 lines
Quoted 6 lines
With the above, how would you know that it is the
req_selortrig_selthat is specified?Quoted 3 lines
#dma-cells=3D<2>:
req_sel,trig_selThe only difference between request and trigger is that trigger issues =
the whole
Quoted 1 line
Quoted 1 line
Quoted 1 line
transfer, while request only a single burst. Isn't it possible to have =
a case in
Quoted 1 line
Quoted 1 line
Quoted 1 line
HW for the "trigger-only" option? If not or it's a rareness, then I agr=
ee that
Quoted 10 lines
Quoted 10 lines
Quoted 1 line
REQ_SELmust be mandatory.I think that what Stephen is proposing is that for now we go with '#dma-cells=3D<1>' and if we ever need to support the trigger cell we could add support for '#dma-cells=3D<2>'. So with this proposal the '
req_sel' would always be required for both '#dma-cells=3D<1>' and '#dma-cells=3D<2>'. Even if thereq_selis not actually used but the 'trig_sel' is, the user would have to set 'req_sel' to some pre-defined value (eg. -1) where we know to ignore it.
Quoted 2 lines
=20
Okay, I see now. Thank you for the clarification, but then we should have=
that
Quoted 1 line
pre-defined value declared in the binding?
I would have thought it should be in the dt-binding header.
Cheers
Jon
--=20
nvpublic
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
In reply to Jon HunterQuoted 84 lines · Jon Hunter
Quoted 81 lines · Dmitry Osipenko
Quoted 80 lines · Stephen Warren
Quoted 68 lines · Dmitry Osipenko
Quoted 56 lines · Stephen Warren
Quoted 47 lines · Jon Hunter
Quoted 39 lines · Dmitry Osipenko
Quoted 28 lines · Jon Hunter
Quoted 26 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+)create mode 100644
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtdiff --git
a/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtb/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtnew file mode 100644
index 000000000000..2af9aa76ae11Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +7
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt. +- clocks: Should contain one entry, DMA controller clock.Quoted 9 lines
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +2
-0+- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request selectvalue
+ for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register +REQ_SELfield.What about the
TRIG_SELfield? Do we need to handle this here as well?Quoted 3 lines
Actually, DMA transfer trigger isn't related a hardware description. It's up to
software to decide what trigger to select. So it shouldn't be in the binding.I think it could be, if say a board wanted a GPIO to trigger a transfer.
Quoted 7 lines
Quoted 1 line
And I think the same applies to requester... any objections?
Well, the
REQ_SELshould definitely be in the binding.Laxman, Stephen, what are your thoughts on the
TRIG_SELfield? Looks like we never bothered with it for the APB DMA and so maybe no ones uses this.Quoted 10 lines
I don't think
TRIG_SELshould be in the binding, at least at present. WhileTRIG_SELcertainly is something used to configure the transfer, I believe the semantics of the current DMA binding only cover DMA transfers that are initiated when SW desires, rather than being a combination of after SW programs the transfer plus some other HW event. So, we always use a default/hard-codedTRIG_SELvalue. As such, there's no need for aTRIG_SELvalue in DT. There's certainly no known use-case that requires a non-defaultTRIG_SELvalue at present. We could add an extra #dma-cells value later if we find a use for it, and the semantics of that use-case make sense to add it to the DMA specifier, rather than some other separate higher-level property/driver/...Quoted 7 lines
Thank you for the comment. If we'd want to extend the binding further with the trigger, how to differentiate trigger from the requester in a case of a single #data-cell?
Of course realistically a chance that the further extension would be needed is very-very low, so we may defer the efforts to solve that question and for now make driver aware of the potential #dma-cells extension.
The request selector cell isn't optional, so is always present. If we later add an optional
trig_selcell, we'll either have:
Quoted 7 lines
Quoted 7 lines
Quoted 5 lines
#dma-cells=<1>:
req_selor:
#dma-cells=<2>:
req_sel,trig_selWhy request sel. couldn't be optional? Could you please elaborate a bit more?
The documentation currently says it's mandatory, and DT bindings must be evolved in a backwards-compatible fashion.
Quoted 7 lines
Quoted 4 lines
I think possible options are:
#dma-cells=<1>:
req_sel
#dma-cells=<1>:trig_selWith the above, how would you know that it is the
req_selortrig_selthat is specified?
Also, if req_sel were optional, then it'd be impossible to distinguish between those cases, so we can't design a binding like that. In general, when adding extra optional cells to an #xxx-cells style binding, then whenever cell N is present, all cells before cell N must be present even if optional.
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 104 lines · Stephen Warren
Quoted 87 lines · Jon Hunter
Quoted 84 lines · Dmitry Osipenko
Quoted 83 lines · Stephen Warren
Quoted 71 lines · Dmitry Osipenko
Quoted 57 lines · Stephen Warren
Quoted 48 lines · Jon Hunter
Quoted 39 lines · Dmitry Osipenko
Quoted 28 lines · Jon Hunter
Quoted 26 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>---
.../bindings/dma/nvidia,tegra20-ahbdma.txt | 23
++++++++++++++++++++++1 file changed, 23 insertions(+)create mode 100644
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtdiff --git
a/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtb/Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtnew file mode 100644
index 000000000000..2af9aa76ae11Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +7
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt. +- clocks: Should contain one entry, DMA controller clock.Quoted 9 lines
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +2
-0+- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request selectvalue
+ for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register +REQ_SELfield.What about the
TRIG_SELfield? Do we need to handle this here as well?Quoted 4 lines
Actually, DMA transfer trigger isn't related a hardware description. It's up to
software to decide what trigger to select. So it shouldn't be in the binding.I think it could be, if say a board wanted a GPIO to trigger a transfer.
Quoted 7 lines
Quoted 1 line
And I think the same applies to requester... any objections?
Well, the
REQ_SELshould definitely be in the binding.Laxman, Stephen, what are your thoughts on the
TRIG_SELfield? Looks like we never bothered with it for the APB DMA and so maybe no ones uses this.Quoted 12 lines
I don't think
TRIG_SELshould be in the binding, at least at present. WhileTRIG_SELcertainly is something used to configure the transfer, I believe the semantics of the current DMA binding only cover DMA transfers that are initiated
when SW desires, rather than being a combination of after SW programs the transfer plus some other HW event. So, we always use a default/hard-codedTRIG_SELvalue. As such, there's no need for aTRIG_SELvalue in DT. There's certainly no known use-case that requires a non-defaultTRIG_SELvalue at present. We could add an extra #dma-cells value later if we find a use for it,
and the semantics of that use-case make sense to add it to the DMA specifier, rather than some other separate higher-level property/driver/...Quoted 7 lines
Thank you for the comment. If we'd want to extend the binding further with the trigger, how to differentiate trigger from the requester in a case of a single #data-cell?
Of course realistically a chance that the further extension would be needed is very-very low, so we may defer the efforts to solve that question and for now make driver aware of the potential #dma-cells extension.
The request selector cell isn't optional, so is always present. If we later add an optional
trig_selcell, we'll either have:Quoted 7 lines
Quoted 7 lines
Quoted 5 lines
#dma-cells=<1>:
req_selor:
#dma-cells=<2>:
req_sel,trig_selWhy request sel. couldn't be optional? Could you please elaborate a bit more?
The documentation currently says it's mandatory, and DT bindings must be evolved in a backwards-compatible fashion.
Quoted 5 lines
Quoted 4 lines
I think possible options are:
#dma-cells=<1>:
req_sel
#dma-cells=<1>:trig_sel
Quoted 7 lines
Quoted 2 lines
With the above, how would you know that it is the
req_selortrig_selthat is specified?Also, if
req_selwere optional, then it'd be impossible to distinguish between those cases, so we can't design a binding like that. In general, when adding extra optional cells to an #xxx-cells style binding, then whenever cell N is present, all cells before cell N must be present even if optional.
I've checked how extending of #dma-cells looks like and it is indeed a good variant since it preserves backward compatibility. Thank you and Jon for the comments and suggestions, I'll send out v2 soon.
Rob Herring
maintainerRe: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
In reply to Dmitry OsipenkoQuoted 21 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtDocumentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt.
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 16
Quoted 10 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +10
-0+- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's + documentation, in particular AHB DMA channel control register + REQ_SEL field. + +Example: + +ahbdma: ahbdma@60008000 {
Use standard node names. dma-controller in this case.
Quoted 10 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +7
-0+ compatible = "nvidia,tegra20-ahbdma"; + reg = <0x60008000 0x2000>; + interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&tegra_car TEGRA20_CLK_AHBDMA>; + resets = <&tegra_car 33>; + #dma-cells = <1>; +};Signature · 1 line
2.14.1
Re: [PATCH v1 3/5] dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
Quoted 26 lines · Rob Herring
Quoted 25 lines · Dmitry Osipenko
Document DT bindings for NVIDIA Tegra AHB DMA controller that presents on Tegra20/30 SoC's.
Signed-off-by Dmitry Osipenko <[email protected]>--- .../bindings/dma/nvidia,tegra20-ahbdma.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txtDocumentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +10
-0@@ -0,0 +1,23 @@ +* NVIDIA Tegra AHB DMA controller + +Required properties: +- compatible: Must be "nvidia,tegra20-ahbdma" +- reg: Should contain registers base address and length. +- interrupts: Should contain one entry, DMA controller interrupt. +- clocks: Should contain one entry, DMA controller clock. +- resets : Should contain one entry, DMA controller reset. +- #dma-cells: Should be <1>. The cell represents DMA request select value + for the peripheral. For more details consult the Tegra TRM's
Quoted 9 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt line 16
Quoted 6 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +6
-0+ documentation, in particular AHB DMA channel control register + REQ_SEL field. + +Example: + +ahbdma: ahbdma@60008000 {Use standard node names. dma-controller in this case.
Okay, I'll change it in v3. Thank you for the comment.
Quoted 10 lines
Quoted 10 lines
Documentation/devicetree/bindings/dma/nvidia,tegra20-ahbdma.txt new file +7
-0+ compatible = "nvidia,tegra20-ahbdma"; + reg = <0x60008000 0x2000>; + interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&tegra_car TEGRA20_CLK_AHBDMA>; + resets = <&tegra_car 33>; + #dma-cells = <1>; +};Signature · 1 line
2.14.1
PATCH 4/5 dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
In reply to Dmitry OsipenkoAHB DMA controller presents on Tegra20/30 SoC's, it supports transfers memory <-> AHB bus peripherals as well as mem-to-mem transfers. Driver doesn't yet implement transfers larger than 64K and scatter-gather transfers that have NENT > 1, HW doesn't have native support for these cases.
--- drivers/dma/Kconfig | 9 + drivers/dma/Makefile | 1 + drivers/dma/tegra20-ahb-dma.c | 679 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 689 insertions(+) create mode 100644 drivers/dma/tegra20-ahb-dma.c
drivers/dma/Kconfig +9 -0
@@ -503,6 +503,15 @@ config TXX9_DMAC Support the TXx9 SoC internal DMA controller. This can be integrated in chips such as the Toshiba TX4927/38/39. +config TEGRA20_AHB_DMA + tristate "NVIDIA Tegra20 AHB DMA support" + depends on ARCH_TEGRA + select DMA_ENGINE + help + Enable support for the NVIDIA Tegra20 AHB DMA controller driver. + This DMA controller transfers data from memory to AHB peripherals + or vice versa, it supports memory to memory data transfer as well. + config TEGRA20_APB_DMA bool "NVIDIA Tegra20 APB DMA support" depends on ARCH_TEGRA
drivers/dma/Makefile +1 -0
@@ -61,6 +61,7 @@ obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o obj-$(CONFIG_STM32_DMA) += stm32-dma.o obj-$(CONFIG_S3C24XX_DMAC) += s3c24xx-dma.o obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o +obj-$(CONFIG_TEGRA20_AHB_DMA) += tegra20-ahb-dma.o obj-$(CONFIG_TEGRA20_APB_DMA) += tegra20-apb-dma.o obj-$(CONFIG_TEGRA210_ADMA) += tegra210-adma.o obj-$(CONFIG_TIMB_DMA) += timb_dma.o
drivers/dma/tegra20-ahb-dma.c new file +679 -0
679 lines not on this page · raw message
Signature · 1 line
2.14.1
Jon Hunter
maintainerRe: [PATCH v1 4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
Hi Dmitry,
Quoted 13 lines · Dmitry Osipenko
AHB DMA controller presents on Tegra20/30 SoC's, it supports transfers memory <-> AHB bus peripherals as well as mem-to-mem transfers. Driver doesn't yet implement transfers larger than 64K and scatter-gather transfers that have NENT > 1, HW doesn't have native support for these cases.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/dma/Kconfig | 9 + drivers/dma/Makefile | 1 + drivers/dma/tegra20-ahb-dma.c | 679 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 689 insertions(+) create mode 100644 drivers/dma/tegra20-ahb-dma.c
...
Quoted 67 lines
drivers/dma/tegra20-ahb-dma.c new file +61
-0@@ -0,0 +1,679 @@ +/* +* Copyright 2017 Dmitry Osipenko <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_dma.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include "dmaengine.h" + +#define TEGRA_AHBDMA_CMD 0x0 +#define TEGRA_AHBDMA_CMD_ENABLE BIT(31) + +#define TEGRA_AHBDMA_IRQ_ENB_MASK 0x20 +#define TEGRA_AHBDMA_IRQ_ENB_CH (ch) BIT(ch) + +#define TEGRA_AHBDMA_CHANNEL_BASE (ch) (0x1000 + (ch) * 0x20) + +#define TEGRA_AHBDMA_CHANNEL_CSR 0x0 +#define TEGRA_AHBDMA_CHANNEL_ADDR_WRAP BIT(18) +#define TEGRA_AHBDMA_CHANNEL_FLOW BIT(24) +#define TEGRA_AHBDMA_CHANNEL_ONCE BIT(26) +#define TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB BIT(27) +#define TEGRA_AHBDMA_CHANNEL_IE_EOC BIT(30) +#define TEGRA_AHBDMA_CHANNEL_ENABLE BIT(31) +#define TEGRA_AHBDMA_CHANNEL_REQ_SEL_SHIFT 16 +#define TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK 0xFFFC + +#define TEGRA_AHBDMA_CHANNEL_STA 0x4 +#define TEGRA_AHBDMA_CHANNEL_IS_EOC BIT(30) + +#define TEGRA_AHBDMA_CHANNEL_AHB_PTR 0x10 + +#define TEGRA_AHBDMA_CHANNEL_AHB_SEQ 0x14 +#define TEGRA_AHBDMA_CHANNEL_INTR_ENB BIT(31) +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT 24 +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_1 2 +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_4 3 +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_8 4 + +#define TEGRA_AHBDMA_CHANNEL_XMB_PTR 0x18
drivers/dma/tegra20-ahb-dma.c line 71
Quoted 10 lines
drivers/dma/tegra20-ahb-dma.c new file +10
-0+ +#define TEGRA_AHBDMA_BUS_WIDTH BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) + +#define TEGRA_AHBDMA_DIRECTIONS BIT(DMA_DEV_TO_MEM) | \ + BIT(DMA_MEM_TO_DEV) + +struct tegra_ahbdma_tx_desc { +struct dma_async_tx_descriptor desc; +struct tasklet_struct tasklet; +struct list_head node;
Any reason why we cannot use the virt-dma framework for this driver? I would hope it would simplify the driver a bit.
Quoted 427 lines
drivers/dma/tegra20-ahb-dma.c new file +427
-0+enum dma_transfer_direction dir; +dma_addr_t mem_paddr; +unsigned long flags; +size_t size; +bool in_fly; +bool cyclic; +}; + +struct tegra_ahbdma_chan { +struct dma_chan dma_chan; +struct list_head active_list; +struct list_head pending_list; +struct completion idling; +void __iomem *regs; +spinlock_t lock; +unsigned int id; +}; + +struct tegra_ahbdma { +struct tegra_ahbdma_chan channels[4 ]; +struct dma_device dma_dev; +struct reset_control *rst; +struct clk *clk; +void __iomem *regs; +}; + +static inline struct tegra_ahbdma *to_ahbdma (struct dma_device *dev) +{ + return container_of(dev, struct tegra_ahbdma, dma_dev); +} + +static inline struct tegra_ahbdma_chan *to_ahbdma_chan (struct dma_chan *chan) +{ + return container_of(chan, struct tegra_ahbdma_chan, dma_chan); +} + +static inline struct tegra_ahbdma_tx_desc *to_ahbdma_tx_desc ( +struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct tegra_ahbdma_tx_desc, desc); +} + +static void tegra_ahbdma_submit_tx (struct tegra_ahbdma_chan *chan, +struct tegra_ahbdma_tx_desc *tx) +{ +u32 csr; + +writel_relaxed (tx->mem_paddr, + chan->regs +TEGRA_AHBDMA_CHANNEL_XMB_PTR ); + + csr =readl_relaxed (chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + + csr &= ~TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK ; + csr &= ~TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB ; + csr |=TEGRA_AHBDMA_CHANNEL_ENABLE ; + csr |=TEGRA_AHBDMA_CHANNEL_IE_EOC ; + csr |= tx->size -sizeof (u32); + +if (tx->dir ==DMA_DEV_TO_MEM ) + csr |=TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB ; + +if (!tx->cyclic) + csr |=TEGRA_AHBDMA_CHANNEL_ONCE ; + +writel_relaxed (csr, chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + + tx->in_fly = true; +} + +static void tegra_ahbdma_tasklet (unsigned long data) +{ +struct tegra_ahbdma_tx_desc *tx = (struct tegra_ahbdma_tx_desc *)data; +struct dma_async_tx_descriptor *desc = &tx->desc; + +dmaengine_desc_get_callback_invoke (desc,NULL ); + +if (!tx->cyclic && !dmaengine_desc_test_reuse (desc)) +kfree (tx); +} + +static bool tegra_ahbdma_tx_completed (struct tegra_ahbdma_chan *chan, +struct tegra_ahbdma_tx_desc *tx) +{ +struct dma_async_tx_descriptor *desc = &tx->desc; +bool reuse =dmaengine_desc_test_reuse (desc); +bool interrupt = tx->flags &DMA_PREP_INTERRUPT ; +bool completed = !tx->cyclic; + +if (completed) +dma_cookie_complete (desc); + +if (interrupt) +tasklet_schedule (&tx->tasklet); + +if (completed) { +list_del (&tx->node); + +if (reuse) + tx->in_fly = false; + +if (!interrupt && !reuse) +kfree (tx); + } + +return completed; +} + +static bool tegra_ahbdma_next_tx_issued (struct tegra_ahbdma_chan *chan) +{ +struct tegra_ahbdma_tx_desc *tx; + + tx =list_first_entry_or_null (&chan->active_list, + struct tegra_ahbdma_tx_desc, + node); +if (tx) +tegra_ahbdma_submit_tx (chan, tx); + +return !!tx; +} + +static void tegra_ahbdma_handle_channel (struct tegra_ahbdma_chan *chan) +{ +struct tegra_ahbdma_tx_desc *tx; +unsigned long flags; +u32 status; + + status =readl_relaxed (chan->regs +TEGRA_AHBDMA_CHANNEL_STA ); +if (!(status &TEGRA_AHBDMA_CHANNEL_IS_EOC )) +return ; + +writel_relaxed (TEGRA_AHBDMA_CHANNEL_IS_EOC , + chan->regs +TEGRA_AHBDMA_CHANNEL_STA ); + +spin_lock_irqsave (&chan->lock, flags); + +if (!completion_done (&chan->idling)) { + tx =list_first_entry (&chan->active_list, + struct tegra_ahbdma_tx_desc, + node); + +if (tegra_ahbdma_tx_completed (chan, tx) && + !tegra_ahbdma_next_tx_issued (chan)) +complete_all (&chan->idling); + } + +spin_unlock_irqrestore (&chan->lock, flags); +} + +static irqreturn_t tegra_ahbdma_isr (int irq,void *dev_id) +{ +struct tegra_ahbdma *tdma = dev_id; +unsigned int i; + +for (i =0 ; i <ARRAY_SIZE (tdma->channels); i++) +tegra_ahbdma_handle_channel (&tdma->channels[i]); + +return IRQ_HANDLED ; +} + +static dma_cookie_t tegra_ahbdma_tx_submit (struct dma_async_tx_descriptor *desc) +{ +struct tegra_ahbdma_tx_desc *tx =to_ahbdma_tx_desc (desc); +struct tegra_ahbdma_chan *chan =to_ahbdma_chan (desc->chan); +dma_cookie_t cookie; + + cookie =dma_cookie_assign (desc); + +spin_lock_irq (&chan->lock); +list_add_tail (&tx->node, &chan->pending_list); +spin_unlock_irq (&chan->lock); + +return cookie; +} + +static int tegra_ahbdma_tx_desc_free (struct dma_async_tx_descriptor *desc) +{ +kfree (to_ahbdma_tx_desc (desc)); + +return 0 ; +} + +static struct dma_async_tx_descriptor *tegra_ahbdma_prep_slave_sg ( +struct dma_chan *chan, +struct scatterlist *sgl, +unsigned int sg_len, +enum dma_transfer_direction dir, +unsigned long flags, +void *context) +{ +struct tegra_ahbdma_tx_desc *tx; + +/* unimplemented */ +if (sg_len !=1 ||sg_dma_len (sgl) >SZ_64K ) +return NULL ; + + tx =kzalloc (sizeof (*tx),GFP_KERNEL ); +if (!tx) +return NULL ; + +dma_async_tx_descriptor_init (&tx->desc, chan); + + tx->desc.tx_submit = tegra_ahbdma_tx_submit; + tx->desc.desc_free = tegra_ahbdma_tx_desc_free; + tx->mem_paddr =sg_dma_address (sgl); + tx->size =sg_dma_len (sgl); + tx->flags = flags; + tx->dir = dir; + +tasklet_init (&tx->tasklet, tegra_ahbdma_tasklet, (unsigned long )tx); + +return &tx->desc; +} + +static struct dma_async_tx_descriptor *tegra_ahbdma_prep_dma_cyclic ( +struct dma_chan *chan, +dma_addr_t buf_addr, +size_t buf_len, +size_t period_len, +enum dma_transfer_direction dir, +unsigned long flags) +{ +struct tegra_ahbdma_tx_desc *tx; + +/* unimplemented */ +if (buf_len != period_len || buf_len >SZ_64K ) +return NULL ; + + tx =kzalloc (sizeof (*tx),GFP_KERNEL ); +if (!tx) +return NULL ; + +dma_async_tx_descriptor_init (&tx->desc, chan); + + tx->desc.tx_submit = tegra_ahbdma_tx_submit; + tx->mem_paddr = buf_addr; + tx->size = buf_len; + tx->flags = flags; + tx->cyclic = true; + tx->dir = dir; + +tasklet_init (&tx->tasklet, tegra_ahbdma_tasklet, (unsigned long )tx); + +return &tx->desc; +} + +static void tegra_ahbdma_issue_pending (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; +unsigned long flags; + +spin_lock_irqsave (&ahbdma_chan->lock, flags); + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) +list_move_tail (entry, &ahbdma_chan->active_list); + +if (completion_done (&ahbdma_chan->idling)) { + tx =list_first_entry_or_null (&ahbdma_chan->active_list, + struct tegra_ahbdma_tx_desc, + node); +if (tx) { +tegra_ahbdma_submit_tx (ahbdma_chan, tx); +reinit_completion (&ahbdma_chan->idling); + } + } + +spin_unlock_irqrestore (&ahbdma_chan->lock, flags); +} + +static enum dma_status tegra_ahbdma_tx_status (struct dma_chan *chan, +dma_cookie_t cookie, +struct dma_tx_state *state) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +enum dma_status cookie_status; +unsigned long flags; +size_t residual; +u32 status; + +spin_lock_irqsave (&ahbdma_chan->lock, flags); + + cookie_status =dma_cookie_status (chan, cookie, state); +if (cookie_status !=DMA_COMPLETE ) { +list_for_each_entry (tx, &ahbdma_chan->active_list, node) { +if (tx->desc.cookie == cookie) + goto found; + } + } + + goto unlock; + +found: +if (tx->in_fly) { + status =readl_relaxed ( + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_STA ); + status &=TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK ; + + residual = status; + }else + residual = tx->size; + +dma_set_residue (state, residual); + +unlock: +spin_unlock_irqrestore (&ahbdma_chan->lock, flags); + +return cookie_status; +} + +static int tegra_ahbdma_terminate_all (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; +u32 csr; + +spin_lock_irq (&ahbdma_chan->lock); + + csr =readl_relaxed (ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + csr &= ~TEGRA_AHBDMA_CHANNEL_ENABLE ; + +writel_relaxed (csr, ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + +list_for_each_safe (entry, tmp, &ahbdma_chan->active_list) { + tx = list_entry(entry, struct tegra_ahbdma_tx_desc, node); +list_del (entry); +kfree (tx); + } + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) { + tx = list_entry(entry, struct tegra_ahbdma_tx_desc, node); +list_del (entry); +kfree (tx); + } + +complete_all (&ahbdma_chan->idling); + +spin_unlock_irq (&ahbdma_chan->lock); + +return 0 ; +} + +static int tegra_ahbdma_config (struct dma_chan *chan, +struct dma_slave_config *sconfig) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +enum dma_transfer_direction dir = sconfig->direction; +u32 burst, ahb_seq, ahb_addr; + +if (sconfig->src_addr_width !=DMA_SLAVE_BUSWIDTH_4_BYTES || + sconfig->dst_addr_width !=DMA_SLAVE_BUSWIDTH_4_BYTES ) +return -EINVAL ; + +if (dir ==DMA_DEV_TO_MEM ) { + burst = sconfig->src_maxburst; + ahb_addr = sconfig->src_addr; + }else { + burst = sconfig->dst_maxburst; + ahb_addr = sconfig->dst_addr; + } + +switch (burst) { +case 1 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_1 ;break ; +case 4 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_4 ;break ; +case 8 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_8 ;break ; +default : +return -EINVAL ; + } + + ahb_seq = burst <<TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_ADDR_WRAP ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_INTR_ENB ; + +writel_relaxed (ahb_seq, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_SEQ ); + +writel_relaxed (ahb_addr, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_PTR ); + +return 0 ; +} + +static void tegra_ahbdma_synchronize (struct dma_chan *chan) +{ +wait_for_completion (&to_ahbdma_chan (chan)->idling); +} + +static void tegra_ahbdma_free_chan_resources (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) { + tx = list_entry(entry, struct tegra_ahbdma_tx_desc, node); +list_del (entry); +kfree (tx); + } +} + +static void tegra_ahbdma_init_channel (struct tegra_ahbdma *tdma, +unsigned int chan_id) +{ +struct tegra_ahbdma_chan *ahbdma_chan = &tdma->channels[chan_id]; +struct dma_chan *dma_chan = &ahbdma_chan->dma_chan; +struct dma_device *dma_dev = &tdma->dma_dev; + +INIT_LIST_HEAD (&ahbdma_chan->active_list); +INIT_LIST_HEAD (&ahbdma_chan->pending_list); +init_completion (&ahbdma_chan->idling); +spin_lock_init (&ahbdma_chan->lock); +complete (&ahbdma_chan->idling); + + ahbdma_chan->regs = tdma->regs +TEGRA_AHBDMA_CHANNEL_BASE (chan_id); + ahbdma_chan->id = chan_id; + +dma_cookie_init (dma_chan); + dma_chan->device = dma_dev; + +list_add_tail (&dma_chan->device_node, &dma_dev->channels); +} + +static struct dma_chan *tegra_ahbdma_of_xlate (struct of_phandle_args *dma_spec, +struct of_dma *ofdma) +{
drivers/dma/tegra20-ahb-dma.c line 508
Quoted 10 lines
drivers/dma/tegra20-ahb-dma.c new file +10
-0+struct tegra_ahbdma *tdma = ofdma->of_dma_data; +struct dma_chan *chan; +u32 csr; + + chan =dma_get_any_slave_channel (&tdma->dma_dev); +if (!chan) +return NULL ; + +/* enable channels flow control */ +if (dma_spec->args_count ==1 ) {
The DT doc says #dma-cells should be '1' and so if not equal 1, is this not an error?
drivers/dma/tegra20-ahb-dma.c line 510
Quoted 2 lines
drivers/dma/tegra20-ahb-dma.c new file +2
-0+ csr =TEGRA_AHBDMA_CHANNEL_FLOW ; + csr |= dma_spec->args[0 ] <<TEGRA_AHBDMA_CHANNEL_REQ_SEL_SHIFT ;
What about the TRIG_REQ field?
Quoted 32 lines
drivers/dma/tegra20-ahb-dma.c new file +32
-0+ +writel_relaxed (csr, +to_ahbdma_chan (chan)->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + } + +return chan; +} + +static int tegra_ahbdma_init_hw (struct tegra_ahbdma *tdma,struct device *dev) +{ +int err; + + err =reset_control_assert (tdma->rst); +if (err) { +dev_err (dev,"Failed to assert reset: %d\n" , err); +return err; + } + + err =clk_prepare_enable (tdma->clk); +if (err) { +dev_err (dev,"Failed to enable clock: %d\n" , err); +return err; + } + +usleep_range (1000 ,2000 ); + + err =reset_control_deassert (tdma->rst); +if (err) { +dev_err (dev,"Failed to deassert reset: %d\n" , err); +return err; + } +
drivers/dma/tegra20-ahb-dma.c line 552
Quoted 10 lines
drivers/dma/tegra20-ahb-dma.c new file +10
-0+writel_relaxed (TEGRA_AHBDMA_CMD_ENABLE , tdma->regs +TEGRA_AHBDMA_CMD ); + +writel_relaxed (TEGRA_AHBDMA_IRQ_ENB_CH (0 ) | +TEGRA_AHBDMA_IRQ_ENB_CH (1 ) | +TEGRA_AHBDMA_IRQ_ENB_CH (2 ) | +TEGRA_AHBDMA_IRQ_ENB_CH (3 ), + tdma->regs +TEGRA_AHBDMA_IRQ_ENB_MASK ); + +return 0 ; +}
Personally I would use the pm_runtime callbacks for this sort of thing and ...
Quoted 39 lines
drivers/dma/tegra20-ahb-dma.c new file +39
-0+static int tegra_ahbdma_probe (struct platform_device *pdev) +{ +struct dma_device *dma_dev; +struct tegra_ahbdma *tdma; +struct resource *res_regs; +unsigned int i; +int irq; +int err; + + tdma =devm_kzalloc (&pdev->dev,sizeof (*tdma),GFP_KERNEL ); +if (!tdma) +return -ENOMEM ; + + irq =platform_get_irq (pdev,0 ); +if (irq <0 ) { +dev_err (&pdev->dev,"Failed to get IRQ\n" ); +return irq; + } + + err =devm_request_irq (&pdev->dev, irq, tegra_ahbdma_isr,0 , +dev_name (&pdev->dev), tdma); +if (err) { +dev_err (&pdev->dev,"Failed to request IRQ\n" ); +return -ENODEV ; + } + + res_regs =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +if (!res_regs) +return -ENODEV ; + + tdma->regs =devm_ioremap_resource (&pdev->dev, res_regs); +if (IS_ERR (tdma->regs)) +return PTR_ERR (tdma->regs); + + tdma->clk =devm_clk_get (&pdev->dev,NULL ); +if (IS_ERR (tdma->clk)) { +dev_err (&pdev->dev,"Failed to get AHB-DMA clock\n" ); +return PTR_ERR (tdma->clk); + }
drivers/dma/tegra20-ahb-dma.c line 602
Quoted 10 lines
drivers/dma/tegra20-ahb-dma.c new file +10
-0+ + tdma->rst =devm_reset_control_get (&pdev->dev,NULL ); +if (IS_ERR (tdma->rst)) { +dev_err (&pdev->dev,"Failed to get AHB-DMA reset\n" ); +return PTR_ERR (tdma->rst); + } + + err =tegra_ahbdma_init_hw (tdma, &pdev->dev); +if (err) +return err;
... here is looks like we turn the clocks on and leave them on. I would rather that we turn them on when the DMA channel is requested and turn them off again when freed. Again would be good to use pm_runtime APIs for this.
Quoted 57 lines
drivers/dma/tegra20-ahb-dma.c new file +57
-0+ dma_dev = &tdma->dma_dev; + +INIT_LIST_HEAD (&dma_dev->channels); + +for (i =0 ; i <ARRAY_SIZE (tdma->channels); i++) +tegra_ahbdma_init_channel (tdma, i); + +dma_cap_set (DMA_PRIVATE , dma_dev->cap_mask); +dma_cap_set (DMA_CYCLIC , dma_dev->cap_mask); +dma_cap_set (DMA_SLAVE , dma_dev->cap_mask); + + dma_dev->max_burst =8 ; + dma_dev->directions =TEGRA_AHBDMA_DIRECTIONS ; + dma_dev->src_addr_widths =TEGRA_AHBDMA_BUS_WIDTH ; + dma_dev->dst_addr_widths =TEGRA_AHBDMA_BUS_WIDTH ; + dma_dev->descriptor_reuse = true; + dma_dev->residue_granularity =DMA_RESIDUE_GRANULARITY_BURST ; + dma_dev->device_free_chan_resources = tegra_ahbdma_free_chan_resources; + dma_dev->device_prep_slave_sg = tegra_ahbdma_prep_slave_sg; + dma_dev->device_prep_dma_cyclic = tegra_ahbdma_prep_dma_cyclic; + dma_dev->device_terminate_all = tegra_ahbdma_terminate_all; + dma_dev->device_issue_pending = tegra_ahbdma_issue_pending; + dma_dev->device_tx_status = tegra_ahbdma_tx_status; + dma_dev->device_config = tegra_ahbdma_config; + dma_dev->device_synchronize = tegra_ahbdma_synchronize; + dma_dev->dev = &pdev->dev; + + err =dma_async_device_register (dma_dev); +if (err) { +dev_err (&pdev->dev,"Device registration failed %d\n" , err); +return err; + } + + err =of_dma_controller_register (pdev->dev.of_node, + tegra_ahbdma_of_xlate, tdma); +if (err) { +dev_err (&pdev->dev,"OF registration failed %d\n" , err); +dma_async_device_unregister (dma_dev); +return err; + } + +platform_set_drvdata (pdev, tdma); + +return 0 ; +} + +static int tegra_ahbdma_remove (struct platform_device *pdev) +{ +struct tegra_ahbdma *tdma =platform_get_drvdata (pdev); + +of_dma_controller_free (pdev->dev.of_node); +dma_async_device_unregister (&tdma->dma_dev); +clk_disable_unprepare (tdma->clk); + +return 0 ; +} +
drivers/dma/tegra20-ahb-dma.c line 670
Quoted 10 lines
drivers/dma/tegra20-ahb-dma.c new file +10
-0+static const struct of_device_id tegra_ahbdma_of_match[] = { + { .compatible ="nvidia,tegra20-ahbdma" }, + { }, +}; +MODULE_DEVICE_TABLE (of, tegra_ahbdma_of_match); + +static struct platform_driver tegra_ahbdma_driver = { + .driver = { + .name ="tegra-ahbdma" , + .of_match_table = tegra_ahbdma_of_match,
It would be nice to have suspend/resume handler too. We could do a similar thing to the APB dma driver.
drivers/dma/tegra20-ahb-dma.c line 679
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c new file +9
-0+ }, + .probe = tegra_ahbdma_probe, + .remove = tegra_ahbdma_remove, +}; +module_platform_driver (tegra_ahbdma_driver); + +MODULE_DESCRIPTION ("NVIDIA Tegra AHB DMA Controller driver" ); +MODULE_AUTHOR ("Dmitry Osipenko <[email protected]>" ); +MODULE_LICENSE ("GPL" );
Cheers
Jon
Signature · 1 line
nvpublic
Re: [PATCH v1 4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
Hi Jon,
Quoted 91 lines · Jon Hunter
Hi Dmitry,
Quoted 13 lines · Dmitry Osipenko
AHB DMA controller presents on Tegra20/30 SoC's, it supports transfers memory <-> AHB bus peripherals as well as mem-to-mem transfers. Driver doesn't yet implement transfers larger than 64K and scatter-gather transfers that have NENT > 1, HW doesn't have native support for these cases.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/dma/Kconfig | 9 + drivers/dma/Makefile | 1 + drivers/dma/tegra20-ahb-dma.c | 679 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 689 insertions(+) create mode 100644 drivers/dma/tegra20-ahb-dma.c...
Quoted 72 lines
drivers/dma/tegra20-ahb-dma.c new file +66
-0@@ -0,0 +1,679 @@ +/* +* Copyright 2017 Dmitry Osipenko <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_dma.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include "dmaengine.h" + +#define TEGRA_AHBDMA_CMD 0x0 +#define TEGRA_AHBDMA_CMD_ENABLE BIT(31) + +#define TEGRA_AHBDMA_IRQ_ENB_MASK 0x20 +#define TEGRA_AHBDMA_IRQ_ENB_CH (ch) BIT(ch) + +#define TEGRA_AHBDMA_CHANNEL_BASE (ch) (0x1000 + (ch) * 0x20) + +#define TEGRA_AHBDMA_CHANNEL_CSR 0x0 +#define TEGRA_AHBDMA_CHANNEL_ADDR_WRAP BIT(18) +#define TEGRA_AHBDMA_CHANNEL_FLOW BIT(24) +#define TEGRA_AHBDMA_CHANNEL_ONCE BIT(26) +#define TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB BIT(27) +#define TEGRA_AHBDMA_CHANNEL_IE_EOC BIT(30) +#define TEGRA_AHBDMA_CHANNEL_ENABLE BIT(31) +#define TEGRA_AHBDMA_CHANNEL_REQ_SEL_SHIFT 16 +#define TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK 0xFFFC + +#define TEGRA_AHBDMA_CHANNEL_STA 0x4 +#define TEGRA_AHBDMA_CHANNEL_IS_EOC BIT(30) + +#define TEGRA_AHBDMA_CHANNEL_AHB_PTR 0x10 + +#define TEGRA_AHBDMA_CHANNEL_AHB_SEQ 0x14 +#define TEGRA_AHBDMA_CHANNEL_INTR_ENB BIT(31) +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT 24 +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_1 2 +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_4 3 +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_8 4 + +#define TEGRA_AHBDMA_CHANNEL_XMB_PTR 0x18 + +#define TEGRA_AHBDMA_BUS_WIDTH BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) + +#define TEGRA_AHBDMA_DIRECTIONS BIT(DMA_DEV_TO_MEM) | \ + BIT(DMA_MEM_TO_DEV)
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 71
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+ +struct tegra_ahbdma_tx_desc { +struct dma_async_tx_descriptor desc; +struct tasklet_struct tasklet; +struct list_head node;Any reason why we cannot use the virt-dma framework for this driver? I would hope it would simplify the driver a bit.
IIUC virt-dma is supposed to provide virtually unlimited number of channels. I've looked at it and decided that it would just add unnecessary functionality and, as a result, complexity. As I wrote in the cover-letter, it is supposed that this driver would have only one consumer - the host1x. It shouldn't be difficult to implement virt-dma later, if desired. But again it is very unlikely that it would be needed.
Quoted 432 lines
Quoted 432 lines
drivers/dma/tegra20-ahb-dma.c new file +432
-0+enum dma_transfer_direction dir; +dma_addr_t mem_paddr; +unsigned long flags; +size_t size; +bool in_fly; +bool cyclic; +}; + +struct tegra_ahbdma_chan { +struct dma_chan dma_chan; +struct list_head active_list; +struct list_head pending_list; +struct completion idling; +void __iomem *regs; +spinlock_t lock; +unsigned int id; +}; + +struct tegra_ahbdma { +struct tegra_ahbdma_chan channels[4 ]; +struct dma_device dma_dev; +struct reset_control *rst; +struct clk *clk; +void __iomem *regs; +}; + +static inline struct tegra_ahbdma *to_ahbdma (struct dma_device *dev) +{ + return container_of(dev, struct tegra_ahbdma, dma_dev); +} + +static inline struct tegra_ahbdma_chan *to_ahbdma_chan (struct dma_chan *chan) +{ + return container_of(chan, struct tegra_ahbdma_chan, dma_chan); +} + +static inline struct tegra_ahbdma_tx_desc *to_ahbdma_tx_desc ( +struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct tegra_ahbdma_tx_desc, desc); +} + +static void tegra_ahbdma_submit_tx (struct tegra_ahbdma_chan *chan, +struct tegra_ahbdma_tx_desc *tx) +{ +u32 csr; + +writel_relaxed (tx->mem_paddr, + chan->regs +TEGRA_AHBDMA_CHANNEL_XMB_PTR ); + + csr =readl_relaxed (chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + + csr &= ~TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK ; + csr &= ~TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB ; + csr |=TEGRA_AHBDMA_CHANNEL_ENABLE ; + csr |=TEGRA_AHBDMA_CHANNEL_IE_EOC ; + csr |= tx->size -sizeof (u32); + +if (tx->dir ==DMA_DEV_TO_MEM ) + csr |=TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB ; + +if (!tx->cyclic) + csr |=TEGRA_AHBDMA_CHANNEL_ONCE ; + +writel_relaxed (csr, chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + + tx->in_fly = true; +} + +static void tegra_ahbdma_tasklet (unsigned long data) +{ +struct tegra_ahbdma_tx_desc *tx = (struct tegra_ahbdma_tx_desc *)data; +struct dma_async_tx_descriptor *desc = &tx->desc; + +dmaengine_desc_get_callback_invoke (desc,NULL ); + +if (!tx->cyclic && !dmaengine_desc_test_reuse (desc)) +kfree (tx); +} + +static bool tegra_ahbdma_tx_completed (struct tegra_ahbdma_chan *chan, +struct tegra_ahbdma_tx_desc *tx) +{ +struct dma_async_tx_descriptor *desc = &tx->desc; +bool reuse =dmaengine_desc_test_reuse (desc); +bool interrupt = tx->flags &DMA_PREP_INTERRUPT ; +bool completed = !tx->cyclic; + +if (completed) +dma_cookie_complete (desc); + +if (interrupt) +tasklet_schedule (&tx->tasklet); + +if (completed) { +list_del (&tx->node); + +if (reuse) + tx->in_fly = false; + +if (!interrupt && !reuse) +kfree (tx); + } + +return completed; +} + +static bool tegra_ahbdma_next_tx_issued (struct tegra_ahbdma_chan *chan) +{ +struct tegra_ahbdma_tx_desc *tx; + + tx =list_first_entry_or_null (&chan->active_list, + struct tegra_ahbdma_tx_desc, + node); +if (tx) +tegra_ahbdma_submit_tx (chan, tx); + +return !!tx; +} + +static void tegra_ahbdma_handle_channel (struct tegra_ahbdma_chan *chan) +{ +struct tegra_ahbdma_tx_desc *tx; +unsigned long flags; +u32 status; + + status =readl_relaxed (chan->regs +TEGRA_AHBDMA_CHANNEL_STA ); +if (!(status &TEGRA_AHBDMA_CHANNEL_IS_EOC )) +return ; + +writel_relaxed (TEGRA_AHBDMA_CHANNEL_IS_EOC , + chan->regs +TEGRA_AHBDMA_CHANNEL_STA ); + +spin_lock_irqsave (&chan->lock, flags); + +if (!completion_done (&chan->idling)) { + tx =list_first_entry (&chan->active_list, + struct tegra_ahbdma_tx_desc, + node); + +if (tegra_ahbdma_tx_completed (chan, tx) && + !tegra_ahbdma_next_tx_issued (chan)) +complete_all (&chan->idling); + } + +spin_unlock_irqrestore (&chan->lock, flags); +} + +static irqreturn_t tegra_ahbdma_isr (int irq,void *dev_id) +{ +struct tegra_ahbdma *tdma = dev_id; +unsigned int i; + +for (i =0 ; i <ARRAY_SIZE (tdma->channels); i++) +tegra_ahbdma_handle_channel (&tdma->channels[i]); + +return IRQ_HANDLED ; +} + +static dma_cookie_t tegra_ahbdma_tx_submit (struct dma_async_tx_descriptor *desc) +{ +struct tegra_ahbdma_tx_desc *tx =to_ahbdma_tx_desc (desc); +struct tegra_ahbdma_chan *chan =to_ahbdma_chan (desc->chan); +dma_cookie_t cookie; + + cookie =dma_cookie_assign (desc); + +spin_lock_irq (&chan->lock); +list_add_tail (&tx->node, &chan->pending_list); +spin_unlock_irq (&chan->lock); + +return cookie; +} + +static int tegra_ahbdma_tx_desc_free (struct dma_async_tx_descriptor *desc) +{ +kfree (to_ahbdma_tx_desc (desc)); + +return 0 ; +} + +static struct dma_async_tx_descriptor *tegra_ahbdma_prep_slave_sg ( +struct dma_chan *chan, +struct scatterlist *sgl, +unsigned int sg_len, +enum dma_transfer_direction dir, +unsigned long flags, +void *context) +{ +struct tegra_ahbdma_tx_desc *tx; + +/* unimplemented */ +if (sg_len !=1 ||sg_dma_len (sgl) >SZ_64K ) +return NULL ; + + tx =kzalloc (sizeof (*tx),GFP_KERNEL ); +if (!tx) +return NULL ; + +dma_async_tx_descriptor_init (&tx->desc, chan); + + tx->desc.tx_submit = tegra_ahbdma_tx_submit; + tx->desc.desc_free = tegra_ahbdma_tx_desc_free; + tx->mem_paddr =sg_dma_address (sgl); + tx->size =sg_dma_len (sgl); + tx->flags = flags; + tx->dir = dir; + +tasklet_init (&tx->tasklet, tegra_ahbdma_tasklet, (unsigned long )tx); + +return &tx->desc; +} + +static struct dma_async_tx_descriptor *tegra_ahbdma_prep_dma_cyclic ( +struct dma_chan *chan, +dma_addr_t buf_addr, +size_t buf_len, +size_t period_len, +enum dma_transfer_direction dir, +unsigned long flags) +{ +struct tegra_ahbdma_tx_desc *tx; + +/* unimplemented */ +if (buf_len != period_len || buf_len >SZ_64K ) +return NULL ; + + tx =kzalloc (sizeof (*tx),GFP_KERNEL ); +if (!tx) +return NULL ; + +dma_async_tx_descriptor_init (&tx->desc, chan); + + tx->desc.tx_submit = tegra_ahbdma_tx_submit; + tx->mem_paddr = buf_addr; + tx->size = buf_len; + tx->flags = flags; + tx->cyclic = true; + tx->dir = dir; + +tasklet_init (&tx->tasklet, tegra_ahbdma_tasklet, (unsigned long )tx); + +return &tx->desc; +} + +static void tegra_ahbdma_issue_pending (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; +unsigned long flags; + +spin_lock_irqsave (&ahbdma_chan->lock, flags); + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) +list_move_tail (entry, &ahbdma_chan->active_list); + +if (completion_done (&ahbdma_chan->idling)) { + tx =list_first_entry_or_null (&ahbdma_chan->active_list, + struct tegra_ahbdma_tx_desc, + node); +if (tx) { +tegra_ahbdma_submit_tx (ahbdma_chan, tx); +reinit_completion (&ahbdma_chan->idling); + } + } + +spin_unlock_irqrestore (&ahbdma_chan->lock, flags); +} + +static enum dma_status tegra_ahbdma_tx_status (struct dma_chan *chan, +dma_cookie_t cookie, +struct dma_tx_state *state) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +enum dma_status cookie_status; +unsigned long flags; +size_t residual; +u32 status; + +spin_lock_irqsave (&ahbdma_chan->lock, flags); + + cookie_status =dma_cookie_status (chan, cookie, state); +if (cookie_status !=DMA_COMPLETE ) { +list_for_each_entry (tx, &ahbdma_chan->active_list, node) { +if (tx->desc.cookie == cookie) + goto found; + } + } + + goto unlock; + +found: +if (tx->in_fly) { + status =readl_relaxed ( + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_STA ); + status &=TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK ; + + residual = status; + }else + residual = tx->size; + +dma_set_residue (state, residual); + +unlock: +spin_unlock_irqrestore (&ahbdma_chan->lock, flags); + +return cookie_status; +} + +static int tegra_ahbdma_terminate_all (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; +u32 csr; + +spin_lock_irq (&ahbdma_chan->lock); + + csr =readl_relaxed (ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + csr &= ~TEGRA_AHBDMA_CHANNEL_ENABLE ; + +writel_relaxed (csr, ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + +list_for_each_safe (entry, tmp, &ahbdma_chan->active_list) { + tx = list_entry(entry, struct tegra_ahbdma_tx_desc, node); +list_del (entry); +kfree (tx); + } + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) { + tx = list_entry(entry, struct tegra_ahbdma_tx_desc, node); +list_del (entry); +kfree (tx); + } + +complete_all (&ahbdma_chan->idling); + +spin_unlock_irq (&ahbdma_chan->lock); + +return 0 ; +} + +static int tegra_ahbdma_config (struct dma_chan *chan, +struct dma_slave_config *sconfig) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +enum dma_transfer_direction dir = sconfig->direction; +u32 burst, ahb_seq, ahb_addr; + +if (sconfig->src_addr_width !=DMA_SLAVE_BUSWIDTH_4_BYTES || + sconfig->dst_addr_width !=DMA_SLAVE_BUSWIDTH_4_BYTES ) +return -EINVAL ; + +if (dir ==DMA_DEV_TO_MEM ) { + burst = sconfig->src_maxburst; + ahb_addr = sconfig->src_addr; + }else { + burst = sconfig->dst_maxburst; + ahb_addr = sconfig->dst_addr; + } + +switch (burst) { +case 1 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_1 ;break ; +case 4 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_4 ;break ; +case 8 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_8 ;break ; +default : +return -EINVAL ; + } + + ahb_seq = burst <<TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_ADDR_WRAP ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_INTR_ENB ; + +writel_relaxed (ahb_seq, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_SEQ ); + +writel_relaxed (ahb_addr, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_PTR ); + +return 0 ; +} + +static void tegra_ahbdma_synchronize (struct dma_chan *chan) +{ +wait_for_completion (&to_ahbdma_chan (chan)->idling); +} + +static void tegra_ahbdma_free_chan_resources (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) { + tx = list_entry(entry, struct tegra_ahbdma_tx_desc, node); +list_del (entry); +kfree (tx); + } +} + +static void tegra_ahbdma_init_channel (struct tegra_ahbdma *tdma, +unsigned int chan_id) +{ +struct tegra_ahbdma_chan *ahbdma_chan = &tdma->channels[chan_id]; +struct dma_chan *dma_chan = &ahbdma_chan->dma_chan; +struct dma_device *dma_dev = &tdma->dma_dev; + +INIT_LIST_HEAD (&ahbdma_chan->active_list); +INIT_LIST_HEAD (&ahbdma_chan->pending_list); +init_completion (&ahbdma_chan->idling); +spin_lock_init (&ahbdma_chan->lock); +complete (&ahbdma_chan->idling); + + ahbdma_chan->regs = tdma->regs +TEGRA_AHBDMA_CHANNEL_BASE (chan_id); + ahbdma_chan->id = chan_id; + +dma_cookie_init (dma_chan); + dma_chan->device = dma_dev; + +list_add_tail (&dma_chan->device_node, &dma_dev->channels); +} + +static struct dma_chan *tegra_ahbdma_of_xlate (struct of_phandle_args *dma_spec, +struct of_dma *ofdma) +{ +struct tegra_ahbdma *tdma = ofdma->of_dma_data; +struct dma_chan *chan; +u32 csr; + + chan =dma_get_any_slave_channel (&tdma->dma_dev);
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 508
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+if (!chan) +return NULL ; + +/* enable channels flow control */ +if (dma_spec->args_count ==1 ) {The DT doc says #dma-cells should be '1' and so if not equal 1, is this not an error?
I wanted to differentiate slave/master modes here. But if we'd want to add TRIG_SEL as another cell, then it probably would worth to implement a custom DMA configure options, like documentation suggests - to wrap generic dma_slave_config into the custom one. On the other hand that probably would add an unused functionality to the driver.
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c line 510
Quoted 2 lines
drivers/dma/tegra20-ahb-dma.c new file +2
-0+ csr =TEGRA_AHBDMA_CHANNEL_FLOW ; + csr |= dma_spec->args[0 ] <<TEGRA_AHBDMA_CHANNEL_REQ_SEL_SHIFT ;What about the
TRIG_REQfield?
Not implemented, there is no test case for it yet.
Quoted 37 lines
Quoted 37 lines
drivers/dma/tegra20-ahb-dma.c new file +37
-0+ +writel_relaxed (csr, +to_ahbdma_chan (chan)->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + } + +return chan; +} + +static int tegra_ahbdma_init_hw (struct tegra_ahbdma *tdma,struct device *dev) +{ +int err; + + err =reset_control_assert (tdma->rst); +if (err) { +dev_err (dev,"Failed to assert reset: %d\n" , err); +return err; + } + + err =clk_prepare_enable (tdma->clk); +if (err) { +dev_err (dev,"Failed to enable clock: %d\n" , err); +return err; + } + +usleep_range (1000 ,2000 ); + + err =reset_control_deassert (tdma->rst); +if (err) { +dev_err (dev,"Failed to deassert reset: %d\n" , err); +return err; + } + +writel_relaxed (TEGRA_AHBDMA_CMD_ENABLE , tdma->regs +TEGRA_AHBDMA_CMD ); + + writel_relaxed(TEGRA_AHBDMA_IRQ_ENB_CH(0) | + TEGRA_AHBDMA_IRQ_ENB_CH(1) | + TEGRA_AHBDMA_IRQ_ENB_CH(2) |
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 552
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+TEGRA_AHBDMA_IRQ_ENB_CH (3 ), + tdma->regs + TEGRA_AHBDMA_IRQ_ENB_MASK); + +return 0 ; +}Personally I would use the
pm_runtimecallbacks for this sort of thing and ...
I decided that it probaby would be better to implement PM later if needed. I'm not sure whether DMA controller consumes any substantial amounts of power while idling. If it's not, why bother? Unnecessary power managment would just cause CPU to waste its cycles (and power) doing PM.
Quoted 46 lines
Quoted 46 lines
drivers/dma/tegra20-ahb-dma.c new file +46
-0+static int tegra_ahbdma_probe (struct platform_device *pdev) +{ +struct dma_device *dma_dev; +struct tegra_ahbdma *tdma; +struct resource *res_regs; +unsigned int i; +int irq; +int err; + + tdma =devm_kzalloc (&pdev->dev,sizeof (*tdma),GFP_KERNEL ); +if (!tdma) +return -ENOMEM ; + + irq =platform_get_irq (pdev,0 ); +if (irq <0 ) { +dev_err (&pdev->dev,"Failed to get IRQ\n" ); +return irq; + } + + err =devm_request_irq (&pdev->dev, irq, tegra_ahbdma_isr,0 , +dev_name (&pdev->dev), tdma); +if (err) { +dev_err (&pdev->dev,"Failed to request IRQ\n" ); +return -ENODEV ; + } + + res_regs =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +if (!res_regs) +return -ENODEV ; + + tdma->regs =devm_ioremap_resource (&pdev->dev, res_regs); +if (IS_ERR (tdma->regs)) +return PTR_ERR (tdma->regs); + + tdma->clk =devm_clk_get (&pdev->dev,NULL ); +if (IS_ERR (tdma->clk)) { +dev_err (&pdev->dev,"Failed to get AHB-DMA clock\n" ); +return PTR_ERR (tdma->clk); + } + + tdma->rst =devm_reset_control_get (&pdev->dev,NULL ); +if (IS_ERR (tdma->rst)) { +dev_err (&pdev->dev,"Failed to get AHB-DMA reset\n" ); +return PTR_ERR (tdma->rst); + } +
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 602
Quoted 3 lines
drivers/dma/tegra20-ahb-dma.c new file +3
-0+ err =tegra_ahbdma_init_hw (tdma, &pdev->dev); +if (err) +return err;... here is looks like we turn the clocks on and leave them on. I would rather that we turn them on when the DMA channel is requested and turn them off again when freed. Again would be good to use
pm_runtimeAPIs for this.
Again not sure about it :)
Quoted 62 lines
Quoted 62 lines
drivers/dma/tegra20-ahb-dma.c new file +62
-0+ dma_dev = &tdma->dma_dev; + +INIT_LIST_HEAD (&dma_dev->channels); + +for (i =0 ; i <ARRAY_SIZE (tdma->channels); i++) +tegra_ahbdma_init_channel (tdma, i); + +dma_cap_set (DMA_PRIVATE , dma_dev->cap_mask); +dma_cap_set (DMA_CYCLIC , dma_dev->cap_mask); +dma_cap_set (DMA_SLAVE , dma_dev->cap_mask); + + dma_dev->max_burst =8 ; + dma_dev->directions =TEGRA_AHBDMA_DIRECTIONS ; + dma_dev->src_addr_widths =TEGRA_AHBDMA_BUS_WIDTH ; + dma_dev->dst_addr_widths =TEGRA_AHBDMA_BUS_WIDTH ; + dma_dev->descriptor_reuse = true; + dma_dev->residue_granularity =DMA_RESIDUE_GRANULARITY_BURST ; + dma_dev->device_free_chan_resources = tegra_ahbdma_free_chan_resources; + dma_dev->device_prep_slave_sg = tegra_ahbdma_prep_slave_sg; + dma_dev->device_prep_dma_cyclic = tegra_ahbdma_prep_dma_cyclic; + dma_dev->device_terminate_all = tegra_ahbdma_terminate_all; + dma_dev->device_issue_pending = tegra_ahbdma_issue_pending; + dma_dev->device_tx_status = tegra_ahbdma_tx_status; + dma_dev->device_config = tegra_ahbdma_config; + dma_dev->device_synchronize = tegra_ahbdma_synchronize; + dma_dev->dev = &pdev->dev; + + err =dma_async_device_register (dma_dev); +if (err) { +dev_err (&pdev->dev,"Device registration failed %d\n" , err); +return err; + } + + err =of_dma_controller_register (pdev->dev.of_node, + tegra_ahbdma_of_xlate, tdma); +if (err) { +dev_err (&pdev->dev,"OF registration failed %d\n" , err); +dma_async_device_unregister (dma_dev); +return err; + } + +platform_set_drvdata (pdev, tdma); + +return 0 ; +} + +static int tegra_ahbdma_remove (struct platform_device *pdev) +{ +struct tegra_ahbdma *tdma =platform_get_drvdata (pdev); + +of_dma_controller_free (pdev->dev.of_node); +dma_async_device_unregister (&tdma->dma_dev); +clk_disable_unprepare (tdma->clk); + +return 0 ; +} + +static const struct of_device_id tegra_ahbdma_of_match[] = { + { .compatible ="nvidia,tegra20-ahbdma" }, + { }, +}; +MODULE_DEVICE_TABLE (of, tegra_ahbdma_of_match);
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 670
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+ +static struct platform_driver tegra_ahbdma_driver = { + .driver = { + .name ="tegra-ahbdma" , + .of_match_table = tegra_ahbdma_of_match,It would be nice to have suspend/resume handler too. We could do a similar thing to the APB dma driver.
It is not stricly necessary because LP0 isn't implemented by the core arch. I've tested LP1 and it works fine that way. I'd prefer to implement suspend/resume later, we can't really test it properly without LP0.
Quoted 9 lines
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c new file +9
-0+ }, + .probe = tegra_ahbdma_probe, + .remove = tegra_ahbdma_remove, +}; +module_platform_driver (tegra_ahbdma_driver); + +MODULE_DESCRIPTION ("NVIDIA Tegra AHB DMA Controller driver" ); +MODULE_AUTHOR ("Dmitry Osipenko <[email protected]>" ); +MODULE_LICENSE ("GPL" );
Signature · 1 line
Dmitry
Jon Hunter
maintainerRe: [PATCH v1 4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
Hi Dmitry,
Quoted 104 lines · Dmitry Osipenko
Hi Jon,
Quoted 91 lines · Jon Hunter
Hi Dmitry,
Quoted 13 lines · Dmitry Osipenko
AHB DMA controller presents on Tegra20/30 SoC's, it supports transfers memory <-> AHB bus peripherals as well as mem-to-mem transfers. Driver doesn't yet implement transfers larger than 64K and scatter-gather transfers that have NENT > 1, HW doesn't have native support for these cases.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/dma/Kconfig | 9 + drivers/dma/Makefile | 1 + drivers/dma/tegra20-ahb-dma.c | 679 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 689 insertions(+) create mode 100644 drivers/dma/tegra20-ahb-dma.c...
Quoted 72 lines
drivers/dma/tegra20-ahb-dma.c new file +66
-0@@ -0,0 +1,679 @@ +/* +* Copyright 2017 Dmitry Osipenko <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_dma.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include "dmaengine.h" + +#define TEGRA_AHBDMA_CMD 0x0 +#define TEGRA_AHBDMA_CMD_ENABLE BIT(31) + +#define TEGRA_AHBDMA_IRQ_ENB_MASK 0x20 +#define TEGRA_AHBDMA_IRQ_ENB_CH (ch) BIT(ch) + +#define TEGRA_AHBDMA_CHANNEL_BASE (ch) (0x1000 + (ch) * 0x20) + +#define TEGRA_AHBDMA_CHANNEL_CSR 0x0 +#define TEGRA_AHBDMA_CHANNEL_ADDR_WRAP BIT(18) +#define TEGRA_AHBDMA_CHANNEL_FLOW BIT(24) +#define TEGRA_AHBDMA_CHANNEL_ONCE BIT(26) +#define TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB BIT(27) +#define TEGRA_AHBDMA_CHANNEL_IE_EOC BIT(30) +#define TEGRA_AHBDMA_CHANNEL_ENABLE BIT(31) +#define TEGRA_AHBDMA_CHANNEL_REQ_SEL_SHIFT 16 +#define TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK 0xFFFC + +#define TEGRA_AHBDMA_CHANNEL_STA 0x4 +#define TEGRA_AHBDMA_CHANNEL_IS_EOC BIT(30) + +#define TEGRA_AHBDMA_CHANNEL_AHB_PTR 0x10 + +#define TEGRA_AHBDMA_CHANNEL_AHB_SEQ 0x14 +#define TEGRA_AHBDMA_CHANNEL_INTR_ENB BIT(31) +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT 24 +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_1 2 +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_4 3 +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_8 4 + +#define TEGRA_AHBDMA_CHANNEL_XMB_PTR 0x18 + +#define TEGRA_AHBDMA_BUS_WIDTH BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) + +#define TEGRA_AHBDMA_DIRECTIONS BIT(DMA_DEV_TO_MEM) | \ + BIT(DMA_MEM_TO_DEV)Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 71
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+ +struct tegra_ahbdma_tx_desc { +struct dma_async_tx_descriptor desc; +struct tasklet_struct tasklet; +struct list_head node;Any reason why we cannot use the virt-dma framework for this driver? I would hope it would simplify the driver a bit.
Quoted 6 lines
IIUC virt-dma is supposed to provide virtually unlimited number of channels. I've looked at it and decided that it would just add unnecessary functionality and, as a result, complexity. As I wrote in the cover-letter, it is supposed that this driver would have only one consumer - the host1x. It shouldn't be difficult to implement virt-dma later, if desired. But again it is very unlikely that it would be needed.
I think that the biggest benefit is that is simplifies the linked list management. See the tegra210-adma driver.
Quoted 503 lines
Quoted 432 lines
Quoted 432 lines
drivers/dma/tegra20-ahb-dma.c new file +432
-0+enum dma_transfer_direction dir; +dma_addr_t mem_paddr; +unsigned long flags; +size_t size; +bool in_fly; +bool cyclic; +}; + +struct tegra_ahbdma_chan { +struct dma_chan dma_chan; +struct list_head active_list; +struct list_head pending_list; +struct completion idling; +void __iomem *regs; +spinlock_t lock; +unsigned int id; +}; + +struct tegra_ahbdma { +struct tegra_ahbdma_chan channels[4 ]; +struct dma_device dma_dev; +struct reset_control *rst; +struct clk *clk; +void __iomem *regs; +}; + +static inline struct tegra_ahbdma *to_ahbdma (struct dma_device *dev) +{ + return container_of(dev, struct tegra_ahbdma, dma_dev); +} + +static inline struct tegra_ahbdma_chan *to_ahbdma_chan (struct dma_chan *chan) +{ + return container_of(chan, struct tegra_ahbdma_chan, dma_chan); +} + +static inline struct tegra_ahbdma_tx_desc *to_ahbdma_tx_desc ( +struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct tegra_ahbdma_tx_desc, desc); +} + +static void tegra_ahbdma_submit_tx (struct tegra_ahbdma_chan *chan, +struct tegra_ahbdma_tx_desc *tx) +{ +u32 csr; + +writel_relaxed (tx->mem_paddr, + chan->regs +TEGRA_AHBDMA_CHANNEL_XMB_PTR ); + + csr =readl_relaxed (chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + + csr &= ~TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK ; + csr &= ~TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB ; + csr |=TEGRA_AHBDMA_CHANNEL_ENABLE ; + csr |=TEGRA_AHBDMA_CHANNEL_IE_EOC ; + csr |= tx->size -sizeof (u32); + +if (tx->dir ==DMA_DEV_TO_MEM ) + csr |=TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB ; + +if (!tx->cyclic) + csr |=TEGRA_AHBDMA_CHANNEL_ONCE ; + +writel_relaxed (csr, chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + + tx->in_fly = true; +} + +static void tegra_ahbdma_tasklet (unsigned long data) +{ +struct tegra_ahbdma_tx_desc *tx = (struct tegra_ahbdma_tx_desc *)data; +struct dma_async_tx_descriptor *desc = &tx->desc; + +dmaengine_desc_get_callback_invoke (desc,NULL ); + +if (!tx->cyclic && !dmaengine_desc_test_reuse (desc)) +kfree (tx); +} + +static bool tegra_ahbdma_tx_completed (struct tegra_ahbdma_chan *chan, +struct tegra_ahbdma_tx_desc *tx) +{ +struct dma_async_tx_descriptor *desc = &tx->desc; +bool reuse =dmaengine_desc_test_reuse (desc); +bool interrupt = tx->flags &DMA_PREP_INTERRUPT ; +bool completed = !tx->cyclic; + +if (completed) +dma_cookie_complete (desc); + +if (interrupt) +tasklet_schedule (&tx->tasklet); + +if (completed) { +list_del (&tx->node); + +if (reuse) + tx->in_fly = false; + +if (!interrupt && !reuse) +kfree (tx); + } + +return completed; +} + +static bool tegra_ahbdma_next_tx_issued (struct tegra_ahbdma_chan *chan) +{ +struct tegra_ahbdma_tx_desc *tx; + + tx =list_first_entry_or_null (&chan->active_list, + struct tegra_ahbdma_tx_desc, + node); +if (tx) +tegra_ahbdma_submit_tx (chan, tx); + +return !!tx; +} + +static void tegra_ahbdma_handle_channel (struct tegra_ahbdma_chan *chan) +{ +struct tegra_ahbdma_tx_desc *tx; +unsigned long flags; +u32 status; + + status =readl_relaxed (chan->regs +TEGRA_AHBDMA_CHANNEL_STA ); +if (!(status &TEGRA_AHBDMA_CHANNEL_IS_EOC )) +return ; + +writel_relaxed (TEGRA_AHBDMA_CHANNEL_IS_EOC , + chan->regs +TEGRA_AHBDMA_CHANNEL_STA ); + +spin_lock_irqsave (&chan->lock, flags); + +if (!completion_done (&chan->idling)) { + tx =list_first_entry (&chan->active_list, + struct tegra_ahbdma_tx_desc, + node); + +if (tegra_ahbdma_tx_completed (chan, tx) && + !tegra_ahbdma_next_tx_issued (chan)) +complete_all (&chan->idling); + } + +spin_unlock_irqrestore (&chan->lock, flags); +} + +static irqreturn_t tegra_ahbdma_isr (int irq,void *dev_id) +{ +struct tegra_ahbdma *tdma = dev_id; +unsigned int i; + +for (i =0 ; i <ARRAY_SIZE (tdma->channels); i++) +tegra_ahbdma_handle_channel (&tdma->channels[i]); + +return IRQ_HANDLED ; +} + +static dma_cookie_t tegra_ahbdma_tx_submit (struct dma_async_tx_descriptor *desc) +{ +struct tegra_ahbdma_tx_desc *tx =to_ahbdma_tx_desc (desc); +struct tegra_ahbdma_chan *chan =to_ahbdma_chan (desc->chan); +dma_cookie_t cookie; + + cookie =dma_cookie_assign (desc); + +spin_lock_irq (&chan->lock); +list_add_tail (&tx->node, &chan->pending_list); +spin_unlock_irq (&chan->lock); + +return cookie; +} + +static int tegra_ahbdma_tx_desc_free (struct dma_async_tx_descriptor *desc) +{ +kfree (to_ahbdma_tx_desc (desc)); + +return 0 ; +} + +static struct dma_async_tx_descriptor *tegra_ahbdma_prep_slave_sg ( +struct dma_chan *chan, +struct scatterlist *sgl, +unsigned int sg_len, +enum dma_transfer_direction dir, +unsigned long flags, +void *context) +{ +struct tegra_ahbdma_tx_desc *tx; + +/* unimplemented */ +if (sg_len !=1 ||sg_dma_len (sgl) >SZ_64K ) +return NULL ; + + tx =kzalloc (sizeof (*tx),GFP_KERNEL ); +if (!tx) +return NULL ; + +dma_async_tx_descriptor_init (&tx->desc, chan); + + tx->desc.tx_submit = tegra_ahbdma_tx_submit; + tx->desc.desc_free = tegra_ahbdma_tx_desc_free; + tx->mem_paddr =sg_dma_address (sgl); + tx->size =sg_dma_len (sgl); + tx->flags = flags; + tx->dir = dir; + +tasklet_init (&tx->tasklet, tegra_ahbdma_tasklet, (unsigned long )tx); + +return &tx->desc; +} + +static struct dma_async_tx_descriptor *tegra_ahbdma_prep_dma_cyclic ( +struct dma_chan *chan, +dma_addr_t buf_addr, +size_t buf_len, +size_t period_len, +enum dma_transfer_direction dir, +unsigned long flags) +{ +struct tegra_ahbdma_tx_desc *tx; + +/* unimplemented */ +if (buf_len != period_len || buf_len >SZ_64K ) +return NULL ; + + tx =kzalloc (sizeof (*tx),GFP_KERNEL ); +if (!tx) +return NULL ; + +dma_async_tx_descriptor_init (&tx->desc, chan); + + tx->desc.tx_submit = tegra_ahbdma_tx_submit; + tx->mem_paddr = buf_addr; + tx->size = buf_len; + tx->flags = flags; + tx->cyclic = true; + tx->dir = dir; + +tasklet_init (&tx->tasklet, tegra_ahbdma_tasklet, (unsigned long )tx); + +return &tx->desc; +} + +static void tegra_ahbdma_issue_pending (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; +unsigned long flags; + +spin_lock_irqsave (&ahbdma_chan->lock, flags); + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) +list_move_tail (entry, &ahbdma_chan->active_list); + +if (completion_done (&ahbdma_chan->idling)) { + tx =list_first_entry_or_null (&ahbdma_chan->active_list, + struct tegra_ahbdma_tx_desc, + node); +if (tx) { +tegra_ahbdma_submit_tx (ahbdma_chan, tx); +reinit_completion (&ahbdma_chan->idling); + } + } + +spin_unlock_irqrestore (&ahbdma_chan->lock, flags); +} + +static enum dma_status tegra_ahbdma_tx_status (struct dma_chan *chan, +dma_cookie_t cookie, +struct dma_tx_state *state) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +enum dma_status cookie_status; +unsigned long flags; +size_t residual; +u32 status; + +spin_lock_irqsave (&ahbdma_chan->lock, flags); + + cookie_status =dma_cookie_status (chan, cookie, state); +if (cookie_status !=DMA_COMPLETE ) { +list_for_each_entry (tx, &ahbdma_chan->active_list, node) { +if (tx->desc.cookie == cookie) + goto found; + } + } + + goto unlock; + +found: +if (tx->in_fly) { + status =readl_relaxed ( + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_STA ); + status &=TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK ; + + residual = status; + }else + residual = tx->size; + +dma_set_residue (state, residual); + +unlock: +spin_unlock_irqrestore (&ahbdma_chan->lock, flags); + +return cookie_status; +} + +static int tegra_ahbdma_terminate_all (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; +u32 csr; + +spin_lock_irq (&ahbdma_chan->lock); + + csr =readl_relaxed (ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + csr &= ~TEGRA_AHBDMA_CHANNEL_ENABLE ; + +writel_relaxed (csr, ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + +list_for_each_safe (entry, tmp, &ahbdma_chan->active_list) { + tx = list_entry(entry, struct tegra_ahbdma_tx_desc, node); +list_del (entry); +kfree (tx); + } + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) { + tx = list_entry(entry, struct tegra_ahbdma_tx_desc, node); +list_del (entry); +kfree (tx); + } + +complete_all (&ahbdma_chan->idling); + +spin_unlock_irq (&ahbdma_chan->lock); + +return 0 ; +} + +static int tegra_ahbdma_config (struct dma_chan *chan, +struct dma_slave_config *sconfig) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +enum dma_transfer_direction dir = sconfig->direction; +u32 burst, ahb_seq, ahb_addr; + +if (sconfig->src_addr_width !=DMA_SLAVE_BUSWIDTH_4_BYTES || + sconfig->dst_addr_width !=DMA_SLAVE_BUSWIDTH_4_BYTES ) +return -EINVAL ; + +if (dir ==DMA_DEV_TO_MEM ) { + burst = sconfig->src_maxburst; + ahb_addr = sconfig->src_addr; + }else { + burst = sconfig->dst_maxburst; + ahb_addr = sconfig->dst_addr; + } + +switch (burst) { +case 1 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_1 ;break ; +case 4 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_4 ;break ; +case 8 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_8 ;break ; +default : +return -EINVAL ; + } + + ahb_seq = burst <<TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_ADDR_WRAP ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_INTR_ENB ; + +writel_relaxed (ahb_seq, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_SEQ ); + +writel_relaxed (ahb_addr, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_PTR ); + +return 0 ; +} + +static void tegra_ahbdma_synchronize (struct dma_chan *chan) +{ +wait_for_completion (&to_ahbdma_chan (chan)->idling); +} + +static void tegra_ahbdma_free_chan_resources (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) { + tx = list_entry(entry, struct tegra_ahbdma_tx_desc, node); +list_del (entry); +kfree (tx); + } +} + +static void tegra_ahbdma_init_channel (struct tegra_ahbdma *tdma, +unsigned int chan_id) +{ +struct tegra_ahbdma_chan *ahbdma_chan = &tdma->channels[chan_id]; +struct dma_chan *dma_chan = &ahbdma_chan->dma_chan; +struct dma_device *dma_dev = &tdma->dma_dev; + +INIT_LIST_HEAD (&ahbdma_chan->active_list); +INIT_LIST_HEAD (&ahbdma_chan->pending_list); +init_completion (&ahbdma_chan->idling); +spin_lock_init (&ahbdma_chan->lock); +complete (&ahbdma_chan->idling); + + ahbdma_chan->regs = tdma->regs +TEGRA_AHBDMA_CHANNEL_BASE (chan_id); + ahbdma_chan->id = chan_id; + +dma_cookie_init (dma_chan); + dma_chan->device = dma_dev; + +list_add_tail (&dma_chan->device_node, &dma_dev->channels); +} + +static struct dma_chan *tegra_ahbdma_of_xlate (struct of_phandle_args *dma_spec, +struct of_dma *ofdma) +{ +struct tegra_ahbdma *tdma = ofdma->of_dma_data; +struct dma_chan *chan; +u32 csr; + + chan =dma_get_any_slave_channel (&tdma->dma_dev);Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 508
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+if (!chan) +return NULL ; + +/* enable channels flow control */ +if (dma_spec->args_count ==1 ) {The DT doc says #dma-cells should be '1' and so if not equal 1, is this not an error?
I wanted to differentiate slave/master modes here. But if we'd want to add
TRIG_SELas another cell, then it probably would worth to implement a custom DMA configure options, like documentation suggests - to wrap genericdma_slave_configinto the custom one. On the other hand that probably would add an unused functionality to the driver.Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c line 510
Quoted 2 lines
drivers/dma/tegra20-ahb-dma.c new file +2
-0+ csr =TEGRA_AHBDMA_CHANNEL_FLOW ; + csr |= dma_spec->args[0 ] <<TEGRA_AHBDMA_CHANNEL_REQ_SEL_SHIFT ;What about the
TRIG_REQfield?Not implemented, there is no test case for it yet.
Quoted 37 lines
Quoted 37 lines
drivers/dma/tegra20-ahb-dma.c new file +37
-0+ +writel_relaxed (csr, +to_ahbdma_chan (chan)->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + } + +return chan; +} + +static int tegra_ahbdma_init_hw (struct tegra_ahbdma *tdma,struct device *dev) +{ +int err; + + err =reset_control_assert (tdma->rst); +if (err) { +dev_err (dev,"Failed to assert reset: %d\n" , err); +return err; + } + + err =clk_prepare_enable (tdma->clk); +if (err) { +dev_err (dev,"Failed to enable clock: %d\n" , err); +return err; + } + +usleep_range (1000 ,2000 ); + + err =reset_control_deassert (tdma->rst); +if (err) { +dev_err (dev,"Failed to deassert reset: %d\n" , err); +return err; + } + +writel_relaxed (TEGRA_AHBDMA_CMD_ENABLE , tdma->regs +TEGRA_AHBDMA_CMD ); + + writel_relaxed(TEGRA_AHBDMA_IRQ_ENB_CH(0) | + TEGRA_AHBDMA_IRQ_ENB_CH(1) | + TEGRA_AHBDMA_IRQ_ENB_CH(2) |Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 552
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+TEGRA_AHBDMA_IRQ_ENB_CH (3 ), + tdma->regs + TEGRA_AHBDMA_IRQ_ENB_MASK); + +return 0 ; +}Personally I would use the
pm_runtimecallbacks for this sort of thing and ...
Quoted 4 lines
I decided that it probaby would be better to implement PM later if needed. I'm not sure whether DMA controller consumes any substantial amounts of power while idling. If it's not, why bother? Unnecessary power managment would just cause CPU to waste its cycles (and power) doing PM.
Yes it probably does not but it is easy to do and so even though there are probably a ton of other clocks left running, I still think it is good practice.
Cheers
Jon
Signature · 1 line
nvpublic
Re: [PATCH v1 4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
Quoted 114 lines · Jon Hunter
Hi Dmitry,
Quoted 104 lines · Dmitry Osipenko
Hi Jon,
Quoted 91 lines · Jon Hunter
Hi Dmitry,
Quoted 13 lines · Dmitry Osipenko
AHB DMA controller presents on Tegra20/30 SoC's, it supports transfers memory <-> AHB bus peripherals as well as mem-to-mem transfers. Driver doesn't yet implement transfers larger than 64K and scatter-gather transfers that have NENT > 1, HW doesn't have native support for these cases.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/dma/Kconfig | 9 + drivers/dma/Makefile | 1 + drivers/dma/tegra20-ahb-dma.c | 679 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 689 insertions(+) create mode 100644 drivers/dma/tegra20-ahb-dma.c...
Quoted 72 lines
drivers/dma/tegra20-ahb-dma.c new file +66
-0@@ -0,0 +1,679 @@ +/* +* Copyright 2017 Dmitry Osipenko <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_dma.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include "dmaengine.h" + +#define TEGRA_AHBDMA_CMD 0x0 +#define TEGRA_AHBDMA_CMD_ENABLE BIT(31) + +#define TEGRA_AHBDMA_IRQ_ENB_MASK 0x20 +#define TEGRA_AHBDMA_IRQ_ENB_CH (ch) BIT(ch) + +#define TEGRA_AHBDMA_CHANNEL_BASE (ch) (0x1000 + (ch) * 0x20) + +#define TEGRA_AHBDMA_CHANNEL_CSR 0x0 +#define TEGRA_AHBDMA_CHANNEL_ADDR_WRAP BIT(18) +#define TEGRA_AHBDMA_CHANNEL_FLOW BIT(24) +#define TEGRA_AHBDMA_CHANNEL_ONCE BIT(26) +#define TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB BIT(27) +#define TEGRA_AHBDMA_CHANNEL_IE_EOC BIT(30) +#define TEGRA_AHBDMA_CHANNEL_ENABLE BIT(31) +#define TEGRA_AHBDMA_CHANNEL_REQ_SEL_SHIFT 16 +#define TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK 0xFFFC + +#define TEGRA_AHBDMA_CHANNEL_STA 0x4 +#define TEGRA_AHBDMA_CHANNEL_IS_EOC BIT(30) + +#define TEGRA_AHBDMA_CHANNEL_AHB_PTR 0x10 + +#define TEGRA_AHBDMA_CHANNEL_AHB_SEQ 0x14 +#define TEGRA_AHBDMA_CHANNEL_INTR_ENB BIT(31) +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT 24 +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_1 2 +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_4 3 +#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_8 4 + +#define TEGRA_AHBDMA_CHANNEL_XMB_PTR 0x18 + +#define TEGRA_AHBDMA_BUS_WIDTH BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) + +#define TEGRA_AHBDMA_DIRECTIONS BIT(DMA_DEV_TO_MEM) | \ + BIT(DMA_MEM_TO_DEV)Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 71
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+ +struct tegra_ahbdma_tx_desc { +struct dma_async_tx_descriptor desc; +struct tasklet_struct tasklet; +struct list_head node;Any reason why we cannot use the virt-dma framework for this driver? I would hope it would simplify the driver a bit.
Quoted 6 lines
IIUC virt-dma is supposed to provide virtually unlimited number of channels. I've looked at it and decided that it would just add unnecessary functionality and, as a result, complexity. As I wrote in the cover-letter, it is supposed that this driver would have only one consumer - the host1x. It shouldn't be difficult to implement virt-dma later, if desired. But again it is very unlikely that it would be needed.
Quoted 3 lines
I think that the biggest benefit is that is simplifies the linked list management. See the tegra210-adma driver.
I'll take a more thorough look at it. Thank you for suggestion.
Quoted 503 lines
Quoted 503 lines
Quoted 432 lines
Quoted 432 lines
drivers/dma/tegra20-ahb-dma.c new file +432
-0+enum dma_transfer_direction dir; +dma_addr_t mem_paddr; +unsigned long flags; +size_t size; +bool in_fly; +bool cyclic; +}; + +struct tegra_ahbdma_chan { +struct dma_chan dma_chan; +struct list_head active_list; +struct list_head pending_list; +struct completion idling; +void __iomem *regs; +spinlock_t lock; +unsigned int id; +}; + +struct tegra_ahbdma { +struct tegra_ahbdma_chan channels[4 ]; +struct dma_device dma_dev; +struct reset_control *rst; +struct clk *clk; +void __iomem *regs; +}; + +static inline struct tegra_ahbdma *to_ahbdma (struct dma_device *dev) +{ + return container_of(dev, struct tegra_ahbdma, dma_dev); +} + +static inline struct tegra_ahbdma_chan *to_ahbdma_chan (struct dma_chan *chan) +{ + return container_of(chan, struct tegra_ahbdma_chan, dma_chan); +} + +static inline struct tegra_ahbdma_tx_desc *to_ahbdma_tx_desc ( +struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct tegra_ahbdma_tx_desc, desc); +} + +static void tegra_ahbdma_submit_tx (struct tegra_ahbdma_chan *chan, +struct tegra_ahbdma_tx_desc *tx) +{ +u32 csr; + +writel_relaxed (tx->mem_paddr, + chan->regs +TEGRA_AHBDMA_CHANNEL_XMB_PTR ); + + csr =readl_relaxed (chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + + csr &= ~TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK ; + csr &= ~TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB ; + csr |=TEGRA_AHBDMA_CHANNEL_ENABLE ; + csr |=TEGRA_AHBDMA_CHANNEL_IE_EOC ; + csr |= tx->size -sizeof (u32); + +if (tx->dir ==DMA_DEV_TO_MEM ) + csr |=TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB ; + +if (!tx->cyclic) + csr |=TEGRA_AHBDMA_CHANNEL_ONCE ; + +writel_relaxed (csr, chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + + tx->in_fly = true; +} + +static void tegra_ahbdma_tasklet (unsigned long data) +{ +struct tegra_ahbdma_tx_desc *tx = (struct tegra_ahbdma_tx_desc *)data; +struct dma_async_tx_descriptor *desc = &tx->desc; + +dmaengine_desc_get_callback_invoke (desc,NULL ); + +if (!tx->cyclic && !dmaengine_desc_test_reuse (desc)) +kfree (tx); +} + +static bool tegra_ahbdma_tx_completed (struct tegra_ahbdma_chan *chan, +struct tegra_ahbdma_tx_desc *tx) +{ +struct dma_async_tx_descriptor *desc = &tx->desc; +bool reuse =dmaengine_desc_test_reuse (desc); +bool interrupt = tx->flags &DMA_PREP_INTERRUPT ; +bool completed = !tx->cyclic; + +if (completed) +dma_cookie_complete (desc); + +if (interrupt) +tasklet_schedule (&tx->tasklet); + +if (completed) { +list_del (&tx->node); + +if (reuse) + tx->in_fly = false; + +if (!interrupt && !reuse) +kfree (tx); + } + +return completed; +} + +static bool tegra_ahbdma_next_tx_issued (struct tegra_ahbdma_chan *chan) +{ +struct tegra_ahbdma_tx_desc *tx; + + tx =list_first_entry_or_null (&chan->active_list, + struct tegra_ahbdma_tx_desc, + node); +if (tx) +tegra_ahbdma_submit_tx (chan, tx); + +return !!tx; +} + +static void tegra_ahbdma_handle_channel (struct tegra_ahbdma_chan *chan) +{ +struct tegra_ahbdma_tx_desc *tx; +unsigned long flags; +u32 status; + + status =readl_relaxed (chan->regs +TEGRA_AHBDMA_CHANNEL_STA ); +if (!(status &TEGRA_AHBDMA_CHANNEL_IS_EOC )) +return ; + +writel_relaxed (TEGRA_AHBDMA_CHANNEL_IS_EOC , + chan->regs +TEGRA_AHBDMA_CHANNEL_STA ); + +spin_lock_irqsave (&chan->lock, flags); + +if (!completion_done (&chan->idling)) { + tx =list_first_entry (&chan->active_list, + struct tegra_ahbdma_tx_desc, + node); + +if (tegra_ahbdma_tx_completed (chan, tx) && + !tegra_ahbdma_next_tx_issued (chan)) +complete_all (&chan->idling); + } + +spin_unlock_irqrestore (&chan->lock, flags); +} + +static irqreturn_t tegra_ahbdma_isr (int irq,void *dev_id) +{ +struct tegra_ahbdma *tdma = dev_id; +unsigned int i; + +for (i =0 ; i <ARRAY_SIZE (tdma->channels); i++) +tegra_ahbdma_handle_channel (&tdma->channels[i]); + +return IRQ_HANDLED ; +} + +static dma_cookie_t tegra_ahbdma_tx_submit (struct dma_async_tx_descriptor *desc) +{ +struct tegra_ahbdma_tx_desc *tx =to_ahbdma_tx_desc (desc); +struct tegra_ahbdma_chan *chan =to_ahbdma_chan (desc->chan); +dma_cookie_t cookie; + + cookie =dma_cookie_assign (desc); + +spin_lock_irq (&chan->lock); +list_add_tail (&tx->node, &chan->pending_list); +spin_unlock_irq (&chan->lock); + +return cookie; +} + +static int tegra_ahbdma_tx_desc_free (struct dma_async_tx_descriptor *desc) +{ +kfree (to_ahbdma_tx_desc (desc)); + +return 0 ; +} + +static struct dma_async_tx_descriptor *tegra_ahbdma_prep_slave_sg ( +struct dma_chan *chan, +struct scatterlist *sgl, +unsigned int sg_len, +enum dma_transfer_direction dir, +unsigned long flags, +void *context) +{ +struct tegra_ahbdma_tx_desc *tx; + +/* unimplemented */ +if (sg_len !=1 ||sg_dma_len (sgl) >SZ_64K ) +return NULL ; + + tx =kzalloc (sizeof (*tx),GFP_KERNEL ); +if (!tx) +return NULL ; + +dma_async_tx_descriptor_init (&tx->desc, chan); + + tx->desc.tx_submit = tegra_ahbdma_tx_submit; + tx->desc.desc_free = tegra_ahbdma_tx_desc_free; + tx->mem_paddr =sg_dma_address (sgl); + tx->size =sg_dma_len (sgl); + tx->flags = flags; + tx->dir = dir; + +tasklet_init (&tx->tasklet, tegra_ahbdma_tasklet, (unsigned long )tx); + +return &tx->desc; +} + +static struct dma_async_tx_descriptor *tegra_ahbdma_prep_dma_cyclic ( +struct dma_chan *chan, +dma_addr_t buf_addr, +size_t buf_len, +size_t period_len, +enum dma_transfer_direction dir, +unsigned long flags) +{ +struct tegra_ahbdma_tx_desc *tx; + +/* unimplemented */ +if (buf_len != period_len || buf_len >SZ_64K ) +return NULL ; + + tx =kzalloc (sizeof (*tx),GFP_KERNEL ); +if (!tx) +return NULL ; + +dma_async_tx_descriptor_init (&tx->desc, chan); + + tx->desc.tx_submit = tegra_ahbdma_tx_submit; + tx->mem_paddr = buf_addr; + tx->size = buf_len; + tx->flags = flags; + tx->cyclic = true; + tx->dir = dir; + +tasklet_init (&tx->tasklet, tegra_ahbdma_tasklet, (unsigned long )tx); + +return &tx->desc; +} + +static void tegra_ahbdma_issue_pending (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; +unsigned long flags; + +spin_lock_irqsave (&ahbdma_chan->lock, flags); + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) +list_move_tail (entry, &ahbdma_chan->active_list); + +if (completion_done (&ahbdma_chan->idling)) { + tx =list_first_entry_or_null (&ahbdma_chan->active_list, + struct tegra_ahbdma_tx_desc, + node); +if (tx) { +tegra_ahbdma_submit_tx (ahbdma_chan, tx); +reinit_completion (&ahbdma_chan->idling); + } + } + +spin_unlock_irqrestore (&ahbdma_chan->lock, flags); +} + +static enum dma_status tegra_ahbdma_tx_status (struct dma_chan *chan, +dma_cookie_t cookie, +struct dma_tx_state *state) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +enum dma_status cookie_status; +unsigned long flags; +size_t residual; +u32 status; + +spin_lock_irqsave (&ahbdma_chan->lock, flags); + + cookie_status =dma_cookie_status (chan, cookie, state); +if (cookie_status !=DMA_COMPLETE ) { +list_for_each_entry (tx, &ahbdma_chan->active_list, node) { +if (tx->desc.cookie == cookie) + goto found; + } + } + + goto unlock; + +found: +if (tx->in_fly) { + status =readl_relaxed ( + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_STA ); + status &=TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK ; + + residual = status; + }else + residual = tx->size; + +dma_set_residue (state, residual); + +unlock: +spin_unlock_irqrestore (&ahbdma_chan->lock, flags); + +return cookie_status; +} + +static int tegra_ahbdma_terminate_all (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; +u32 csr; + +spin_lock_irq (&ahbdma_chan->lock); + + csr =readl_relaxed (ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + csr &= ~TEGRA_AHBDMA_CHANNEL_ENABLE ; + +writel_relaxed (csr, ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + +list_for_each_safe (entry, tmp, &ahbdma_chan->active_list) { + tx = list_entry(entry, struct tegra_ahbdma_tx_desc, node); +list_del (entry); +kfree (tx); + } + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) { + tx = list_entry(entry, struct tegra_ahbdma_tx_desc, node); +list_del (entry); +kfree (tx); + } + +complete_all (&ahbdma_chan->idling); + +spin_unlock_irq (&ahbdma_chan->lock); + +return 0 ; +} + +static int tegra_ahbdma_config (struct dma_chan *chan, +struct dma_slave_config *sconfig) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +enum dma_transfer_direction dir = sconfig->direction; +u32 burst, ahb_seq, ahb_addr; + +if (sconfig->src_addr_width !=DMA_SLAVE_BUSWIDTH_4_BYTES || + sconfig->dst_addr_width !=DMA_SLAVE_BUSWIDTH_4_BYTES ) +return -EINVAL ; + +if (dir ==DMA_DEV_TO_MEM ) { + burst = sconfig->src_maxburst; + ahb_addr = sconfig->src_addr; + }else { + burst = sconfig->dst_maxburst; + ahb_addr = sconfig->dst_addr; + } + +switch (burst) { +case 1 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_1 ;break ; +case 4 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_4 ;break ; +case 8 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_8 ;break ; +default : +return -EINVAL ; + } + + ahb_seq = burst <<TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_ADDR_WRAP ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_INTR_ENB ; + +writel_relaxed (ahb_seq, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_SEQ ); + +writel_relaxed (ahb_addr, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_PTR ); + +return 0 ; +} + +static void tegra_ahbdma_synchronize (struct dma_chan *chan) +{ +wait_for_completion (&to_ahbdma_chan (chan)->idling); +} + +static void tegra_ahbdma_free_chan_resources (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) { + tx = list_entry(entry, struct tegra_ahbdma_tx_desc, node); +list_del (entry); +kfree (tx); + } +} + +static void tegra_ahbdma_init_channel (struct tegra_ahbdma *tdma, +unsigned int chan_id) +{ +struct tegra_ahbdma_chan *ahbdma_chan = &tdma->channels[chan_id]; +struct dma_chan *dma_chan = &ahbdma_chan->dma_chan; +struct dma_device *dma_dev = &tdma->dma_dev; + +INIT_LIST_HEAD (&ahbdma_chan->active_list); +INIT_LIST_HEAD (&ahbdma_chan->pending_list); +init_completion (&ahbdma_chan->idling); +spin_lock_init (&ahbdma_chan->lock); +complete (&ahbdma_chan->idling); + + ahbdma_chan->regs = tdma->regs +TEGRA_AHBDMA_CHANNEL_BASE (chan_id); + ahbdma_chan->id = chan_id; + +dma_cookie_init (dma_chan); + dma_chan->device = dma_dev; + +list_add_tail (&dma_chan->device_node, &dma_dev->channels); +} + +static struct dma_chan *tegra_ahbdma_of_xlate (struct of_phandle_args *dma_spec, +struct of_dma *ofdma) +{ +struct tegra_ahbdma *tdma = ofdma->of_dma_data; +struct dma_chan *chan; +u32 csr; + + chan =dma_get_any_slave_channel (&tdma->dma_dev);Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 508
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+if (!chan) +return NULL ; + +/* enable channels flow control */ +if (dma_spec->args_count ==1 ) {The DT doc says #dma-cells should be '1' and so if not equal 1, is this not an error?
I wanted to differentiate slave/master modes here. But if we'd want to add
TRIG_SELas another cell, then it probably would worth to implement a custom DMA configure options, like documentation suggests - to wrap genericdma_slave_configinto the custom one. On the other hand that probably would add an unused functionality to the driver.Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c line 510
Quoted 2 lines
drivers/dma/tegra20-ahb-dma.c new file +2
-0+ csr =TEGRA_AHBDMA_CHANNEL_FLOW ; + csr |= dma_spec->args[0 ] <<TEGRA_AHBDMA_CHANNEL_REQ_SEL_SHIFT ;What about the
TRIG_REQfield?Not implemented, there is no test case for it yet.
Quoted 46 lines
Quoted 32 lines
drivers/dma/tegra20-ahb-dma.c new file +32
-0+ +writel_relaxed (csr, +to_ahbdma_chan (chan)->regs +TEGRA_AHBDMA_CHANNEL_CSR ); + } + +return chan; +} + +static int tegra_ahbdma_init_hw (struct tegra_ahbdma *tdma,struct device *dev) +{ +int err; + + err =reset_control_assert (tdma->rst); +if (err) { +dev_err (dev,"Failed to assert reset: %d\n" , err); +return err; + } + + err =clk_prepare_enable (tdma->clk); +if (err) { +dev_err (dev,"Failed to enable clock: %d\n" , err); +return err; + } + +usleep_range (1000 ,2000 ); + + err =reset_control_deassert (tdma->rst); +if (err) { +dev_err (dev,"Failed to deassert reset: %d\n" , err); +return err; + } +drivers/dma/tegra20-ahb-dma.c line 552
Quoted 10 lines
drivers/dma/tegra20-ahb-dma.c new file +10
-0+writel_relaxed (TEGRA_AHBDMA_CMD_ENABLE , tdma->regs +TEGRA_AHBDMA_CMD ); + +writel_relaxed (TEGRA_AHBDMA_IRQ_ENB_CH (0 ) | +TEGRA_AHBDMA_IRQ_ENB_CH (1 ) | +TEGRA_AHBDMA_IRQ_ENB_CH (2 ) | +TEGRA_AHBDMA_IRQ_ENB_CH (3 ), + tdma->regs +TEGRA_AHBDMA_IRQ_ENB_MASK ); + +return 0 ; +}Personally I would use the
pm_runtimecallbacks for this sort of thing and ...
Quoted 9 lines
Quoted 4 lines
I decided that it probaby would be better to implement PM later if needed. I'm not sure whether DMA controller consumes any substantial amounts of power while idling. If it's not, why bother? Unnecessary power managment would just cause CPU to waste its cycles (and power) doing PM.
Yes it probably does not but it is easy to do and so even though there are probably a ton of other clocks left running, I still think it is good practice.
Okay, I'll take a look into implementing PM. Disabling AHBDMA clock won't stop the actual clock, but only gate it to the controller.
Thank you for the comments!
Signature · 1 line
Dmitry
Re: [PATCH v1 4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
In reply to Dmitry Osipenkodrivers/dma/Kconfig config TXX9_DMAC · line 508
Quoted 3 lines · Dmitry Osipenko
drivers/dma/Kconfig +3
-0+config TEGRA20_AHB_DMA + tristate "NVIDIA Tegra20 AHB DMA support" + depends on ARCH_TEGRA
Can we add COMPILE_TEST, helps me compile drivers
drivers/dma/tegra20-ahb-dma.c line 27
Quoted 11 lines
drivers/dma/tegra20-ahb-dma.c new file +11
-0+#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_dma.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/spinlock.h>
no vchan.h, so i presume we are not using that here, any reason why?
Quoted 10 lines
drivers/dma/tegra20-ahb-dma.c new file +10
-0+ +#include "dmaengine.h" + +#define TEGRA_AHBDMA_CMD 0x0 +#define TEGRA_AHBDMA_CMD_ENABLE BIT(31) + +#define TEGRA_AHBDMA_IRQ_ENB_MASK 0x20 +#define TEGRA_AHBDMA_IRQ_ENB_CH (ch) BIT(ch) + +#define TEGRA_AHBDMA_CHANNEL_BASE (ch) (0x1000 + (ch) * 0x20)
drivers/dma/tegra20-ahb-dma.c line 47
Quoted 10 lines
drivers/dma/tegra20-ahb-dma.c new file +10
-0+ +#define TEGRA_AHBDMA_CHANNEL_CSR 0x0 +#define TEGRA_AHBDMA_CHANNEL_ADDR_WRAP BIT(18) +#define TEGRA_AHBDMA_CHANNEL_FLOW BIT(24) +#define TEGRA_AHBDMA_CHANNEL_ONCE BIT(26) +#define TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB BIT(27) +#define TEGRA_AHBDMA_CHANNEL_IE_EOC BIT(30) +#define TEGRA_AHBDMA_CHANNEL_ENABLE BIT(31) +#define TEGRA_AHBDMA_CHANNEL_REQ_SEL_SHIFT 16 +#define TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK 0xFFFC
GENMASK() ?
drivers/dma/tegra20-ahb-dma.c line 149
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c new file +9
-0+static void tegra_ahbdma_tasklet (unsigned long data) +{ +struct tegra_ahbdma_tx_desc *tx = (struct tegra_ahbdma_tx_desc *)data; +struct dma_async_tx_descriptor *desc = &tx->desc; + +dmaengine_desc_get_callback_invoke (desc,NULL ); + +if (!tx->cyclic && !dmaengine_desc_test_reuse (desc)) +kfree (tx);
lot of code here can be reduced if we use vchan
Quoted 18 lines
drivers/dma/tegra20-ahb-dma.c new file +18
-0+static struct dma_async_tx_descriptor *tegra_ahbdma_prep_dma_cyclic ( +struct dma_chan *chan, +dma_addr_t buf_addr, +size_t buf_len, +size_t period_len, +enum dma_transfer_direction dir, +unsigned long flags) +{ +struct tegra_ahbdma_tx_desc *tx; + +/* unimplemented */ +if (buf_len != period_len || buf_len >SZ_64K ) +return NULL ; + + tx =kzalloc (sizeof (*tx),GFP_KERNEL ); +if (!tx) +return NULL ; +
drivers/dma/tegra20-ahb-dma.c line 312
Quoted 10 lines
drivers/dma/tegra20-ahb-dma.c new file +10
-0+dma_async_tx_descriptor_init (&tx->desc, chan); + + tx->desc.tx_submit = tegra_ahbdma_tx_submit; + tx->mem_paddr = buf_addr; + tx->size = buf_len; + tx->flags = flags; + tx->cyclic = true; + tx->dir = dir; + +tasklet_init (&tx->tasklet, tegra_ahbdma_tasklet, (unsigned long )tx);
why not precalulcate the register settings here. While submitting you are in hot path keeping dmaengine idle so faster you can submit, better the perf
Quoted 8 lines
drivers/dma/tegra20-ahb-dma.c new file +8
-0+static void tegra_ahbdma_issue_pending (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; +unsigned long flags; + +spin_lock_irqsave (&ahbdma_chan->lock, flags);
drivers/dma/tegra20-ahb-dma.c line 334
Quoted 10 lines
drivers/dma/tegra20-ahb-dma.c new file +10
-0+ +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) +list_move_tail (entry, &ahbdma_chan->active_list); + +if (completion_done (&ahbdma_chan->idling)) { + tx =list_first_entry_or_null (&ahbdma_chan->active_list, + struct tegra_ahbdma_tx_desc, + node); +if (tx) { +tegra_ahbdma_submit_tx (ahbdma_chan, tx);
what is chan is already running?
Quoted 12 lines
drivers/dma/tegra20-ahb-dma.c new file +12
-0+reinit_completion (&ahbdma_chan->idling); + } + } + +spin_unlock_irqrestore (&ahbdma_chan->lock, flags); +} + +static enum dma_status tegra_ahbdma_tx_status (struct dma_chan *chan, +dma_cookie_t cookie, +struct dma_tx_state *state) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan);
drivers/dma/tegra20-ahb-dma.c line 356
Quoted 10 lines
drivers/dma/tegra20-ahb-dma.c new file +10
-0+struct tegra_ahbdma_tx_desc *tx; +enum dma_status cookie_status; +unsigned long flags; +size_t residual; +u32 status; + +spin_lock_irqsave (&ahbdma_chan->lock, flags); + + cookie_status =dma_cookie_status (chan, cookie, state); +if (cookie_status !=DMA_COMPLETE ) {
residue can be NULL so check it before proceeding ahead
Quoted 13 lines
drivers/dma/tegra20-ahb-dma.c new file +13
-0+static int tegra_ahbdma_config (struct dma_chan *chan, +struct dma_slave_config *sconfig) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +enum dma_transfer_direction dir = sconfig->direction; +u32 burst, ahb_seq, ahb_addr; + +if (sconfig->src_addr_width !=DMA_SLAVE_BUSWIDTH_4_BYTES || + sconfig->dst_addr_width !=DMA_SLAVE_BUSWIDTH_4_BYTES ) +return -EINVAL ; + +if (dir ==DMA_DEV_TO_MEM ) { + burst = sconfig->src_maxburst;
drivers/dma/tegra20-ahb-dma.c line 438
Quoted 10 lines
drivers/dma/tegra20-ahb-dma.c new file +10
-0+ ahb_addr = sconfig->src_addr; + }else { + burst = sconfig->dst_maxburst; + ahb_addr = sconfig->dst_addr; + } + +switch (burst) { +case 1 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_1 ;break ; +case 4 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_4 ;break ; +case 8 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_8 ;break ;
pls make this statement and break on subsequent lines, readablity matters
drivers/dma/tegra20-ahb-dma.c line 451
Quoted 13 lines
drivers/dma/tegra20-ahb-dma.c new file +13
-0+default : +return -EINVAL ; + } + + ahb_seq = burst <<TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_ADDR_WRAP ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_INTR_ENB ; + +writel_relaxed (ahb_seq, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_SEQ ); + +writel_relaxed (ahb_addr, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_PTR );
oh no, you don't write to HW here. This can be called anytime when you have txn running! You should save these and use them in prep_ calls.
drivers/dma/tegra20-ahb-dma.c line 656
Quoted 7 lines
drivers/dma/tegra20-ahb-dma.c new file +7
-0+static int tegra_ahbdma_remove (struct platform_device *pdev) +{ +struct tegra_ahbdma *tdma =platform_get_drvdata (pdev); + +of_dma_controller_free (pdev->dev.of_node); +dma_async_device_unregister (&tdma->dma_dev); +clk_disable_unprepare (tdma->clk);
not ensuring tasklets are killed and irq is freed so no more tasklets can run? I think that needs to be done...
drivers/dma/tegra20-ahb-dma.c line 679
Quoted 3 lines
drivers/dma/tegra20-ahb-dma.c new file +3
-0+MODULE_DESCRIPTION ("NVIDIA Tegra AHB DMA Controller driver" ); +MODULE_AUTHOR ("Dmitry Osipenko <[email protected]>" ); +MODULE_LICENSE ("GPL" );
MODULE_ALIAS?
Signature · 1 line
~Vinod
Re: [PATCH v1 4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
Quoted 8 lines · Vinod Koul
drivers/dma/Kconfig config TXX9_DMAC · line 508
Quoted 3 lines · Dmitry Osipenko
drivers/dma/Kconfig +3
-0+config TEGRA20_AHB_DMA + tristate "NVIDIA Tegra20 AHB DMA support" + depends on ARCH_TEGRACan we add
COMPILE_TEST, helps me compile drivers
Good point.
Quoted 5 lines
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h>
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 27
Quoted 6 lines
drivers/dma/tegra20-ahb-dma.c new file +6
-0+#include <linux/of_device.h> +#include <linux/of_dma.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/spinlock.h> no
vchan.h, so i presume we are not using that here, any reason why?
Jon Hunter asked the same question, I already reworked driver to use the virt-dma. Turned out it is a really neat helper, -100 lines of driver code.
Quoted 14 lines
Quoted 14 lines
drivers/dma/tegra20-ahb-dma.c new file +14
-0+ +#include "dmaengine.h" + +#define TEGRA_AHBDMA_CMD 0x0 +#define TEGRA_AHBDMA_CMD_ENABLE BIT(31) + +#define TEGRA_AHBDMA_IRQ_ENB_MASK 0x20 +#define TEGRA_AHBDMA_IRQ_ENB_CH (ch) BIT(ch) + +#define TEGRA_AHBDMA_CHANNEL_BASE (ch) (0x1000 + (ch) * 0x20) + +#define TEGRA_AHBDMA_CHANNEL_CSR 0x0 +#define TEGRA_AHBDMA_CHANNEL_ADDR_WRAP BIT(18) +#define TEGRA_AHBDMA_CHANNEL_FLOW BIT(24)
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 47
Quoted 6 lines
drivers/dma/tegra20-ahb-dma.c new file +6
-0+#define TEGRA_AHBDMA_CHANNEL_ONCE BIT(26) +#define TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB BIT(27) +#define TEGRA_AHBDMA_CHANNEL_IE_EOC BIT(30) +#define TEGRA_AHBDMA_CHANNEL_ENABLE BIT(31) +#define TEGRA_AHBDMA_CHANNEL_REQ_SEL_SHIFT 16 +#define TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK 0xFFFC
GENMASK()?
Okay.
Quoted 12 lines
drivers/dma/tegra20-ahb-dma.c line 149
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c new file +9
-0+static void tegra_ahbdma_tasklet (unsigned long data) +{ +struct tegra_ahbdma_tx_desc *tx = (struct tegra_ahbdma_tx_desc *)data; +struct dma_async_tx_descriptor *desc = &tx->desc; + +dmaengine_desc_get_callback_invoke (desc,NULL ); + +if (!tx->cyclic && !dmaengine_desc_test_reuse (desc)) +kfree (tx);lot of code here can be reduced if we use vchan
+1
Quoted 23 lines
Quoted 23 lines
drivers/dma/tegra20-ahb-dma.c new file +23
-0+static struct dma_async_tx_descriptor *tegra_ahbdma_prep_dma_cyclic ( +struct dma_chan *chan, +dma_addr_t buf_addr, +size_t buf_len, +size_t period_len, +enum dma_transfer_direction dir, +unsigned long flags) +{ +struct tegra_ahbdma_tx_desc *tx; + +/* unimplemented */ +if (buf_len != period_len || buf_len >SZ_64K ) +return NULL ; + + tx =kzalloc (sizeof (*tx),GFP_KERNEL ); +if (!tx) +return NULL ; + +dma_async_tx_descriptor_init (&tx->desc, chan); + + tx->desc.tx_submit = tegra_ahbdma_tx_submit; + tx->mem_paddr = buf_addr; + tx->size = buf_len;
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 312
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+ tx->flags = flags; + tx->cyclic = true; + tx->dir = dir; + +tasklet_init (&tx->tasklet, tegra_ahbdma_tasklet, (unsigned long )tx);why not precalulcate the register settings here. While submitting you are in hot path keeping dmaengine idle so faster you can submit, better the perf
I may argue that the perf impact isn't measurable, but I agree that precalculated register value would be a bit cleaner. Thanks for the suggestion.
Quoted 12 lines
Quoted 12 lines
drivers/dma/tegra20-ahb-dma.c new file +12
-0+static void tegra_ahbdma_issue_pending (struct dma_chan *chan) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +struct list_head *entry, *tmp; +unsigned long flags; + +spin_lock_irqsave (&ahbdma_chan->lock, flags); + +list_for_each_safe (entry, tmp, &ahbdma_chan->pending_list) +list_move_tail (entry, &ahbdma_chan->active_list); +
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 334
Quoted 6 lines
drivers/dma/tegra20-ahb-dma.c new file +6
-0+if (completion_done (&ahbdma_chan->idling)) { + tx =list_first_entry_or_null (&ahbdma_chan->active_list, + struct tegra_ahbdma_tx_desc, + node); +if (tx) { +tegra_ahbdma_submit_tx (ahbdma_chan, tx);what is chan is already running?
It can't run here, we just checked whether it is idling. That would be a HW bug.
Quoted 16 lines
Quoted 16 lines
drivers/dma/tegra20-ahb-dma.c new file +16
-0+reinit_completion (&ahbdma_chan->idling); + } + } + +spin_unlock_irqrestore (&ahbdma_chan->lock, flags); +} + +static enum dma_status tegra_ahbdma_tx_status (struct dma_chan *chan, +dma_cookie_t cookie, +struct dma_tx_state *state) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +struct tegra_ahbdma_tx_desc *tx; +enum dma_status cookie_status; +unsigned long flags; +size_t residual;
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 356
Quoted 6 lines
drivers/dma/tegra20-ahb-dma.c new file +6
-0+u32 status; + +spin_lock_irqsave (&ahbdma_chan->lock, flags); + + cookie_status =dma_cookie_status (chan, cookie, state); +if (cookie_status !=DMA_COMPLETE ) {residue can be NULL so check it before proceeding ahead
Yeah, I noticed it too and fixed it in the upcoming V2 yesterday.
Quoted 17 lines
Quoted 17 lines
drivers/dma/tegra20-ahb-dma.c new file +17
-0+static int tegra_ahbdma_config (struct dma_chan *chan, +struct dma_slave_config *sconfig) +{ +struct tegra_ahbdma_chan *ahbdma_chan =to_ahbdma_chan (chan); +enum dma_transfer_direction dir = sconfig->direction; +u32 burst, ahb_seq, ahb_addr; + +if (sconfig->src_addr_width !=DMA_SLAVE_BUSWIDTH_4_BYTES || + sconfig->dst_addr_width !=DMA_SLAVE_BUSWIDTH_4_BYTES ) +return -EINVAL ; + +if (dir ==DMA_DEV_TO_MEM ) { + burst = sconfig->src_maxburst; + ahb_addr = sconfig->src_addr; + }else { + burst = sconfig->dst_maxburst; + ahb_addr = sconfig->dst_addr;
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 438
Quoted 6 lines
drivers/dma/tegra20-ahb-dma.c new file +6
-0+ } + +switch (burst) { +case 1 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_1 ;break ; +case 4 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_4 ;break ; +case 8 : burst =TEGRA_AHBDMA_CHANNEL_AHB_BURST_8 ;break ;pls make this statement and break on subsequent lines, readablity matters
Okay.
Quoted 8 lines
Quoted 8 lines
drivers/dma/tegra20-ahb-dma.c new file +8
-0+default : +return -EINVAL ; + } + + ahb_seq = burst <<TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_ADDR_WRAP ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_INTR_ENB ; +
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 451
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+writel_relaxed (ahb_seq, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_SEQ ); + +writel_relaxed (ahb_addr, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_PTR );oh no, you don't write to HW here. This can be called anytime when you have txn running! You should save these and use them in
prep_calls.
Okay.
Quoted 11 lines
drivers/dma/tegra20-ahb-dma.c line 656
Quoted 7 lines
drivers/dma/tegra20-ahb-dma.c new file +7
-0+static int tegra_ahbdma_remove (struct platform_device *pdev) +{ +struct tegra_ahbdma *tdma =platform_get_drvdata (pdev); + +of_dma_controller_free (pdev->dev.of_node); +dma_async_device_unregister (&tdma->dma_dev); +clk_disable_unprepare (tdma->clk);not ensuring tasklets are killed and irq is freed so no more tasklets can run? I think that needs to be done...
Already fixed in V2 by using vchan_synchronize() that kills tasklet in tegra_ahbdma_synchronize(). DMA core invokes synchronization upon channels resource freeing.
Quoted 6 lines
drivers/dma/tegra20-ahb-dma.c line 679
Quoted 3 lines
drivers/dma/tegra20-ahb-dma.c new file +3
-0+MODULE_DESCRIPTION ("NVIDIA Tegra AHB DMA Controller driver" ); +MODULE_AUTHOR ("Dmitry Osipenko <[email protected]>" ); +MODULE_LICENSE ("GPL" );
MODULE_ALIAS?
Not needed, driver is "OF-only". It's default alias is "tegra20-ahb-dma".
Signature · 1 line
Dmitry
Re: [PATCH v1 4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
In reply to Vinod KoulQuoted 8 lines · Vinod Koul
Quoted 8 lines
drivers/dma/tegra20-ahb-dma.c new file +8
-0+default : +return -EINVAL ; + } + + ahb_seq = burst <<TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_ADDR_WRAP ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_INTR_ENB ; +
Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 451
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+writel_relaxed (ahb_seq, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_SEQ ); + +writel_relaxed (ahb_addr, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_PTR );oh no, you don't write to HW here. This can be called anytime when you have txn running! You should save these and use them in
prep_calls.
BTW, some of the DMA drivers have exactly the same problem. I now see that it is actually documented explicitly in provider.txt, but that's inconsistent across the actual drivers.
Signature · 1 line
Dmitry
Re: [PATCH v1 4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
Quoted 19 lines · Dmitry Osipenko
Quoted 8 lines · Vinod Koul
Quoted 8 lines
drivers/dma/tegra20-ahb-dma.c new file +8
-0+default : +return -EINVAL ; + } + + ahb_seq = burst <<TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_ADDR_WRAP ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_INTR_ENB ; +Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 451
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+writel_relaxed (ahb_seq, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_SEQ ); + +writel_relaxed (ahb_addr, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_PTR );oh no, you don't write to HW here. This can be called anytime when you have txn running! You should save these and use them in
prep_calls.
Quoted 4 lines
BTW, some of the DMA drivers have exactly the same problem. I now see that it is actually documented explicitly in provider.txt, but that's inconsistent across the actual drivers.
Also, shouldn't prep_ and dma_slave_config be protected with locking? I don't see DMA core doing any locking and seems none of the drivers too.
Signature · 1 line
Dmitry
Re: [PATCH v1 4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
Quoted 20 lines · Dmitry Osipenko
Quoted 19 lines · Dmitry Osipenko
Quoted 17 lines · Vinod Koul
drivers/dma/tegra20-ahb-dma.c line 451
Quoted 13 lines
drivers/dma/tegra20-ahb-dma.c new file +13
-0+default : +return -EINVAL ; + } + + ahb_seq = burst <<TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_ADDR_WRAP ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_INTR_ENB ; + +writel_relaxed (ahb_seq, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_SEQ ); + +writel_relaxed (ahb_addr, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_PTR );oh no, you don't write to HW here. This can be called anytime when you have txn running! You should save these and use them in
prep_calls.
Quoted 7 lines
Quoted 4 lines
BTW, some of the DMA drivers have exactly the same problem. I now see that it is actually documented explicitly in provider.txt, but that's inconsistent across the actual drivers.
Also, shouldn't
prep_anddma_slave_configbe protected with locking? I don't see DMA core doing any locking and seems none of the drivers too.
In prep when you modify the list yes (with vchan I suspect that maybe taken care), but in general yes driver needs to do that
Signature · 1 line
~Vinod
Re: [PATCH v1 4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
Quoted 26 lines · Vinod Koul
Quoted 25 lines · Dmitry Osipenko
Quoted 23 lines · Dmitry Osipenko
Quoted 8 lines · Vinod Koul
Quoted 8 lines
drivers/dma/tegra20-ahb-dma.c new file +8
-0+default : +return -EINVAL ; + } + + ahb_seq = burst <<TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_ADDR_WRAP ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_INTR_ENB ; +Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 451
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+writel_relaxed (ahb_seq, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_SEQ ); + +writel_relaxed (ahb_addr, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_PTR );oh no, you don't write to HW here. This can be called anytime when you have txn running! You should save these and use them in
prep_calls.BTW, some of the DMA drivers have exactly the same problem. I now see that it is actually documented explicitly in provider.txt, but that's inconsistent across the actual drivers.
Quoted 6 lines
Quoted 2 lines
Also, shouldn't
prep_anddma_slave_configbe protected with locking? I don't see DMA core doing any locking and seems none of the drivers too.In prep when you modify the list yes (with vchan I suspect that maybe taken care), but in general yes driver needs to do that
I meant that one CPU could modify channels config, while other CPU is preparing the new TX using config that is in process of the modification. On the other hand, this looks like something that DMA client should take care of.
Signature · 1 line
Dmitry
Re: [PATCH v1 4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
In reply to Dmitry OsipenkoQuoted 19 lines · Dmitry Osipenko
Quoted 8 lines · Vinod Koul
Quoted 8 lines
drivers/dma/tegra20-ahb-dma.c new file +8
-0+default : +return -EINVAL ; + } + + ahb_seq = burst <<TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_ADDR_WRAP ; + ahb_seq |=TEGRA_AHBDMA_CHANNEL_INTR_ENB ; +Quoted 9 lines
drivers/dma/tegra20-ahb-dma.c line 451
Quoted 5 lines
drivers/dma/tegra20-ahb-dma.c new file +5
-0+writel_relaxed (ahb_seq, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_SEQ ); + +writel_relaxed (ahb_addr, + ahbdma_chan->regs +TEGRA_AHBDMA_CHANNEL_AHB_PTR );oh no, you don't write to HW here. This can be called anytime when you have txn running! You should save these and use them in
prep_calls.
Quoted 3 lines
BTW, some of the DMA drivers have exactly the same problem. I now see that it is actually documented explicitly in provider.txt, but that's inconsistent across the actual drivers.
yeah they need to be fixed!
Signature · 1 line
~Vinod
PATCH 5/5 ARM: dts: tegra: Add AHB DMA controller nodes
In reply to Dmitry OsipenkoAdd AHB DMA controller nodes to Tegra20/30 DT's.
--- arch/arm/boot/dts/tegra20.dtsi | 9 +++++++++ arch/arm/boot/dts/tegra30.dtsi | 9 +++++++++ 2 files changed, 18 insertions(+)
arch/arm/boot/dts/tegra20.dtsi +9 -0
@@ -200,6 +200,15 @@ reg = <0x60007000 0x1000>; }; + ahbdma: ahbdma@60008000 { + compatible = "nvidia,tegra20-ahbdma"; + reg = <0x60008000 0x2000>; + interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&tegra_car TEGRA20_CLK_AHBDMA>; + resets = <&tegra_car 33>; + #dma-cells = <1>; + }; + apbdma: dma@6000a000 { compatible = "nvidia,tegra20-apbdma"; reg = <0x6000a000 0x1200>;
arch/arm/boot/dts/tegra30.dtsi +9 -0
@@ -291,6 +291,15 @@ reg = <0x60007000 0x1000>; }; + ahbdma: ahbdma@60008000 { + compatible = "nvidia,tegra20-ahbdma"; + reg = <0x60008000 0x2000>; + interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&tegra_car TEGRA30_CLK_AHBDMA>; + resets = <&tegra_car 33>; + #dma-cells = <1>; + }; + apbdma: dma@6000a000 { compatible = "nvidia,tegra30-apbdma", "nvidia,tegra20-apbdma"; reg = <0x6000a000 0x1400>;
Signature · 1 line
2.14.1
Quoted 11 lines · Dmitry Osipenko
NVIDIA Tegra20/30 SoC's have AHB DMA controller. It has 4 DMA channels, supports AHB <-> Memory and Memory <-> Memory transfers, slave / master modes. This driver is primarily supposed to be used by gpu/host1x in a master mode, performing 3D HW context stores.
Dmitry Osipenko (5):
clk: tegra: Add AHB DMA clock entry
clk: tegra: Bump SCLK clock rate to 216MHz on Tegra20
dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
ARM: dts: tegra: Add AHB DMA controller nodes
I don't think they are dependent, so consider sending them separately
Signature · 1 line
~Vinod
Quoted 6 lines · Vinod Koul
Quoted 5 lines · Dmitry Osipenko
NVIDIA Tegra20/30 SoC's have AHB DMA controller. It has 4 DMA channels, supports AHB <-> Memory and Memory <-> Memory transfers, slave / master modes. This driver is primarily supposed to be used by gpu/host1x in a master mode, performing 3D HW context stores.
Quoted 9 lines
Quoted 6 lines
Dmitry Osipenko (5):
clk: tegra: Add AHB DMA clock entry
clk: tegra: Bump SCLK clock rate to 216MHz on Tegra20
dt-bindings: Add DT bindings for NVIDIA Tegra AHB DMA controller
dmaengine: Add driver for NVIDIA Tegra AHB DMA controller
ARM: dts: tegra: Add AHB DMA controller nodesI don't think they are dependent, so consider sending them separately
Well, they are dependent in a sense of making driver usable. Only the "SCLK rate bump" patch isn't strictly needed.
Splitting this series won't cause building failures, but all pieces should be in place for the working driver. So I suppose it is okay if clk patches would get in earlier than the others, I'll split the series.
Thank you for the review.
Signature · 1 line
Dmitry