diff options
author | Stephen Warren <swarren@nvidia.com> | 2011-02-23 16:49:30 -0500 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2011-02-23 17:06:03 -0500 |
commit | ccac05152e7c6a8103b9e7a801bc995180a800fc (patch) | |
tree | 034bc068b7cd9c3042c52f3cf8dc77ccf701c098 /arch/arm/mach-tegra/dma.c | |
parent | ddb7d5d80edb58e8235f1bc6c350eac40bfe85d1 (diff) |
ARM: Tegra: DMA: Fail safe if initialization fails
tegra_dma_init currently simply bails out early if any initialization fails.
This skips various data-structure initialization. In turn, this means that
tegra_dma_allocate_channel can still hand out channels. In this case, when
tegra_dma_free_channel is called, which calls tegra_dma_cancel, the walking
on ch->list will OOPS since the list's next/prev pointers may still be
NULL.
To solve this, add an explicit "initialized" flag, only set this once _init
has fully completed successfully, and have _allocate_channel refuse to hand
out channels if this is not set.
While at it, simplify _init:
* Remove redundant memsets
* Use bitmap_fill to mark all channels as in-use up-front, and remove
some now-redundant bitmap initialization loops.
* Only mark a channel as free once all channel-related initialization has
completed.
Finally, the successful exit path from _init always has ret==0, so just
hard-code that return. The error path still returns ret.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Colin Cross <ccross@android.com>
Diffstat (limited to 'arch/arm/mach-tegra/dma.c')
-rw-r--r-- | arch/arm/mach-tegra/dma.c | 22 |
1 files changed, 10 insertions, 12 deletions
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index bd4f62a613aa..e945ae28ee77 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c | |||
@@ -127,6 +127,7 @@ struct tegra_dma_channel { | |||
127 | 127 | ||
128 | #define NV_DMA_MAX_CHANNELS 32 | 128 | #define NV_DMA_MAX_CHANNELS 32 |
129 | 129 | ||
130 | static bool tegra_dma_initialized; | ||
130 | static DEFINE_MUTEX(tegra_dma_lock); | 131 | static DEFINE_MUTEX(tegra_dma_lock); |
131 | 132 | ||
132 | static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS); | 133 | static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS); |
@@ -352,6 +353,9 @@ struct tegra_dma_channel *tegra_dma_allocate_channel(int mode) | |||
352 | int channel; | 353 | int channel; |
353 | struct tegra_dma_channel *ch = NULL; | 354 | struct tegra_dma_channel *ch = NULL; |
354 | 355 | ||
356 | if (WARN_ON(!tegra_dma_initialized)) | ||
357 | return NULL; | ||
358 | |||
355 | mutex_lock(&tegra_dma_lock); | 359 | mutex_lock(&tegra_dma_lock); |
356 | 360 | ||
357 | /* first channel is the shared channel */ | 361 | /* first channel is the shared channel */ |
@@ -678,6 +682,8 @@ int __init tegra_dma_init(void) | |||
678 | void __iomem *addr; | 682 | void __iomem *addr; |
679 | struct clk *c; | 683 | struct clk *c; |
680 | 684 | ||
685 | bitmap_fill(channel_usage, NV_DMA_MAX_CHANNELS); | ||
686 | |||
681 | c = clk_get_sys("tegra-dma", NULL); | 687 | c = clk_get_sys("tegra-dma", NULL); |
682 | if (IS_ERR(c)) { | 688 | if (IS_ERR(c)) { |
683 | pr_err("Unable to get clock for APB DMA\n"); | 689 | pr_err("Unable to get clock for APB DMA\n"); |
@@ -696,18 +702,9 @@ int __init tegra_dma_init(void) | |||
696 | writel(0xFFFFFFFFul >> (31 - TEGRA_SYSTEM_DMA_CH_MAX), | 702 | writel(0xFFFFFFFFul >> (31 - TEGRA_SYSTEM_DMA_CH_MAX), |
697 | addr + APB_DMA_IRQ_MASK_SET); | 703 | addr + APB_DMA_IRQ_MASK_SET); |
698 | 704 | ||
699 | memset(channel_usage, 0, sizeof(channel_usage)); | ||
700 | memset(dma_channels, 0, sizeof(dma_channels)); | ||
701 | |||
702 | /* Reserve all the channels we are not supposed to touch */ | ||
703 | for (i = 0; i < TEGRA_SYSTEM_DMA_CH_MIN; i++) | ||
704 | __set_bit(i, channel_usage); | ||
705 | |||
706 | for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) { | 705 | for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) { |
707 | struct tegra_dma_channel *ch = &dma_channels[i]; | 706 | struct tegra_dma_channel *ch = &dma_channels[i]; |
708 | 707 | ||
709 | __clear_bit(i, channel_usage); | ||
710 | |||
711 | ch->id = i; | 708 | ch->id = i; |
712 | snprintf(ch->name, TEGRA_DMA_NAME_SIZE, "dma_channel_%d", i); | 709 | snprintf(ch->name, TEGRA_DMA_NAME_SIZE, "dma_channel_%d", i); |
713 | 710 | ||
@@ -726,14 +723,15 @@ int __init tegra_dma_init(void) | |||
726 | goto fail; | 723 | goto fail; |
727 | } | 724 | } |
728 | ch->irq = irq; | 725 | ch->irq = irq; |
726 | |||
727 | __clear_bit(i, channel_usage); | ||
729 | } | 728 | } |
730 | /* mark the shared channel allocated */ | 729 | /* mark the shared channel allocated */ |
731 | __set_bit(TEGRA_SYSTEM_DMA_CH_MIN, channel_usage); | 730 | __set_bit(TEGRA_SYSTEM_DMA_CH_MIN, channel_usage); |
732 | 731 | ||
733 | for (i = TEGRA_SYSTEM_DMA_CH_MAX+1; i < NV_DMA_MAX_CHANNELS; i++) | 732 | tegra_dma_initialized = true; |
734 | __set_bit(i, channel_usage); | ||
735 | 733 | ||
736 | return ret; | 734 | return 0; |
737 | fail: | 735 | fail: |
738 | writel(0, addr + APB_DMA_GEN); | 736 | writel(0, addr + APB_DMA_GEN); |
739 | for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) { | 737 | for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) { |