[ARM] tegra: use dma to read/write fuse registers
Varun Wadekar [Sun, 24 Oct 2010 10:55:32 +0000 (15:55 +0530)]
tegra2 hangs if fuse registers are accessed during an apb dma
operation. war is to use apb dma to read/write fuse registers
instead.

Change-Id: I4d99a1ad56115c0d73e9cd0679cf38f70f922f3d
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>

arch/arm/mach-tegra/common.c
arch/arm/mach-tegra/fuse.c

index d2889d6..2040e03 100644 (file)
@@ -142,7 +142,9 @@ static void tegra_pm_restart(char mode, const char *cmd)
 void __init tegra_init_early(void)
 {
        arm_pm_restart = tegra_pm_restart;
-
+#ifdef CONFIG_TEGRA_SYSTEM_DMA
+       tegra_dma_init();
+#endif
        tegra_init_fuse();
        tegra_gpio_resume_init();
        tegra_init_clock();
index 1fa26d9..3c0d89c 100644 (file)
 
 #include <linux/kernel.h>
 #include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
 
+#include <mach/dma.h>
 #include <mach/iomap.h>
 
 #include "fuse.h"
 #define FUSE_SKU_INFO          0x110
 #define FUSE_SPARE_BIT         0x200
 
+DEFINE_MUTEX(lock);
+
+#ifdef CONFIG_TEGRA_SYSTEM_DMA
+struct tegra_dma_channel *dma;
+u32 *fuse_bb;
+dma_addr_t fuse_bb_phys;
+struct completion rd_wait;
+struct completion wr_wait;
+
+static void fuse_dma_complete(struct tegra_dma_req *req)
+{
+       if (req)
+               req->to_memory ? complete(&rd_wait) : complete(&wr_wait);
+}
+
+static inline u32 fuse_readl(unsigned long offset)
+{
+       struct tegra_dma_req req;
+
+       if (!dma)
+               return -EINVAL;
+
+       mutex_lock(&lock);
+       req.complete = fuse_dma_complete;
+       req.to_memory = 1;
+       req.dest_addr = fuse_bb_phys;
+       req.dest_bus_width = 32;
+       req.dest_wrap = 1;
+       req.source_addr = TEGRA_FUSE_BASE + offset;
+       req.source_bus_width = 32;
+       req.source_wrap = 4;
+       req.req_sel = 0;
+       req.size = 4;
+
+       init_completion(&rd_wait);
+       tegra_dma_enqueue_req(dma, &req);
+       if (wait_for_completion_timeout(&rd_wait, msecs_to_jiffies(50)) == 0) {
+               WARN_ON(1);
+               mutex_unlock(&lock);
+               return 0;
+       }
+
+       mutex_unlock(&lock);
+       return *((u32 *)fuse_bb);
+}
+
+static inline void fuse_writel(u32 value, unsigned long offset)
+{
+       struct tegra_dma_req req;
+
+       if (!dma || !fuse_bb)
+               return;
+
+       mutex_lock(&lock);
+       *((u32 *)fuse_bb) = value;
+       req.complete = fuse_dma_complete;
+       req.to_memory = 0;
+       req.dest_addr = TEGRA_FUSE_BASE + offset;
+       req.dest_wrap = 4;
+       req.dest_bus_width = 32;
+       req.source_addr = fuse_bb_phys;
+       req.source_bus_width = 32;
+       req.source_wrap = 1;
+       req.req_sel = 0;
+       req.size = 4;
+
+       init_completion(&wr_wait);
+       tegra_dma_enqueue_req(dma, &req);
+       if (wait_for_completion_timeout(&wr_wait, msecs_to_jiffies(50)) == 0)
+               WARN_ON(1);
+       mutex_unlock(&lock);
+}
+#else
 static inline u32 fuse_readl(unsigned long offset)
 {
        return readl(IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
@@ -38,6 +117,7 @@ static inline void fuse_writel(u32 value, unsigned long offset)
 {
        writel(value, IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
 }
+#endif
 
 void tegra_init_fuse(void)
 {
@@ -45,6 +125,25 @@ void tegra_init_fuse(void)
        reg |= 1 << 28;
        writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48));
 
+#ifdef CONFIG_TEGRA_SYSTEM_DMA
+       dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT |
+               TEGRA_DMA_SHARED);
+       if (!dma) {
+               pr_err("%s: can not allocate dma channel\n", __func__);
+               return;
+       }
+
+       fuse_bb = dma_alloc_coherent(NULL, sizeof(u32),
+               &fuse_bb_phys, GFP_KERNEL);
+       if (!fuse_bb) {
+               pr_err("%s: can not allocate bounce buffer\n", __func__);
+               tegra_dma_free_channel(dma);
+               dma = NULL;
+               return;
+       }
+       mutex_init(&lock);
+#endif
+
        pr_info("Tegra SKU: %d CPU Process: %d Core Process: %d\n",
                tegra_sku_id(), tegra_cpu_process_id(),
                tegra_core_process_id());