[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>
This commit is contained in:
Varun Wadekar
2010-10-24 16:25:32 +05:30
committed by Colin Cross
parent 1ff693aee0
commit e8903adee3
2 changed files with 102 additions and 4 deletions

View File

@@ -134,15 +134,14 @@ static void tegra_pm_restart(char mode, const char *cmd)
void __init tegra_common_init(void)
{
arm_pm_restart = tegra_pm_restart;
#ifdef CONFIG_TEGRA_SYSTEM_DMA
tegra_dma_init();
#endif
tegra_init_fuse();
tegra_init_clock();
tegra_clk_init_from_table(common_clk_init_table);
tegra_init_power();
tegra_init_cache();
#ifdef CONFIG_TEGRA_SYSTEM_DMA
tegra_dma_init();
#endif
}
static int __init tegra_bootloader_fb_arg(char *options)

View File

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