summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVarun Wadekar <vwadekar@nvidia.com>2010-10-24 16:25:32 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:36:44 -0800
commit736a3cc80864f9887ad04dd8dfe3b4e3ded5e2ce (patch)
tree1b98307219d81b4a56c2cbbddd93ebe68d12b530
parent77f694ab9f19dea29de599999d3f5f21633c09d9 (diff)
[ARM] tegra: use dma to read/write fuse registers
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>
-rw-r--r--arch/arm/mach-tegra/common.c4
-rw-r--r--arch/arm/mach-tegra/fuse.c99
2 files changed, 102 insertions, 1 deletions
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index d2889d6e1ad8..2040e039f3a8 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -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();
diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c
index 1fa26d9a1a68..3c0d89c934d4 100644
--- a/arch/arm/mach-tegra/fuse.c
+++ b/arch/arm/mach-tegra/fuse.c
@@ -19,7 +19,13 @@
#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"
@@ -29,6 +35,79 @@
#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());