diff options
author | Jon Mayo <jmayo@nvidia.com> | 2010-11-10 00:43:19 -0800 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2010-11-15 20:21:45 -0800 |
commit | f91ff9b31a37b11df8636fa886b01dbdc47587f1 (patch) | |
tree | 6b9a676298e5e9827a17999f835ba3908ed2ac2d /arch/arm/mach-tegra/fuse-cache.c | |
parent | b79646f0a0cbfe48558980215794c178d814585e (diff) |
[arm/tegra] cache kfuses on boottegra-10.9.5
Cache the fuse contents early in boot before DMA is active to ensure
exclusive access on that bus. This cache is exposed at
/sys/firmware/fuse/kfuse_raw and it can be read() or mmap()'d.
Bug 741232
Change-Id: I83bc991c89beb837ec22b2e03ceac11ab696cb6f
Reviewed-on: http://git-master/r/10482
Reviewed-by: Jonathan Mayo <jmayo@nvidia.com>
Tested-by: Jonathan Mayo <jmayo@nvidia.com>
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/fuse-cache.c')
-rw-r--r-- | arch/arm/mach-tegra/fuse-cache.c | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/fuse-cache.c b/arch/arm/mach-tegra/fuse-cache.c new file mode 100644 index 000000000000..1e994fb48b40 --- /dev/null +++ b/arch/arm/mach-tegra/fuse-cache.c @@ -0,0 +1,124 @@ +/* + * arch/arm/mach-tegra/fuse-cache.c + * + * Interface to kfuses on Tegra 200 + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <mach/fuse.h> +#include <mach/iomap.h> +#include <ap20/arclk_rst.h> +#include <ap20/arfuse.h> + +/* register definition */ +#define KFUSE_STATE 0x80 +#define KFUSE_STATE_DONE 0x10000 +#define KFUSE_STATE_CRCPASS 0x20000 +#define KFUSE_KEYADDR 0x88 +#define KFUSE_KEYADDR_AUTOINC 0x10000 +#define KFUSE_KEYADDR_ADDR(addr) (addr) +#define KFUSE_KEYS 0x8c + +#define KFUSE_CACHE_SZ (144 * 4) + +static u32 *kfuse_cache; +static int kfuse_cache_isvalid; + +/* set start address in auto-increment mode */ +static inline void kfuse_set_autoinc_addr(u16 addr) +{ + writel(KFUSE_KEYADDR_ADDR(0) | KFUSE_KEYADDR_AUTOINC, + IO_ADDRESS(TEGRA_KFUSE_BASE) + KFUSE_KEYADDR); +} + +static inline void kfuse_clock_enable(int e) +{ + if (e) { + writel(CLK_RST_CONTROLLER_CLK_ENB_H_SET_0_SET_CLK_ENB_KFUSE_FIELD, + IO_ADDRESS(TEGRA_CLK_RESET_BASE + + CLK_RST_CONTROLLER_CLK_ENB_H_SET_0)); + } else { + writel(CLK_RST_CONTROLLER_CLK_ENB_H_CLR_0_CLR_CLK_ENB_KFUSE_FIELD, + IO_ADDRESS(TEGRA_CLK_RESET_BASE + + CLK_RST_CONTROLLER_CLK_ENB_H_CLR_0)); + } +} + +static void kfuse_wait_ready(void) +{ + int retries = 1; + /* wait for hardware to finish loading and verifying key data */ + do { + u32 val; + val = readl(IO_ADDRESS(TEGRA_KFUSE_BASE) + KFUSE_STATE); + if ((val & KFUSE_STATE_DONE) == KFUSE_STATE_DONE) { + /* hardware does CRC check */ + kfuse_cache_isvalid = (val & KFUSE_STATE_CRCPASS) == KFUSE_STATE_CRCPASS; + break; + } + msleep(10); + } while( --retries >= 0 ); +} + +static void kfuse_rewind(void) +{ + /* force HW to decode and check fuses if it has not already done so */ + kfuse_set_autoinc_addr(0); + kfuse_wait_ready(); + // kfuse_set_autoinc_addr(0); +} + +static inline u32 kfuse_read(void) +{ + return readl(IO_ADDRESS(TEGRA_KFUSE_BASE) + KFUSE_KEYS); +} + +/* this is called very early in init because there is a bug that can cause + * corruption if DMA and non-DMA requests overlap on APB bus. */ +void __init tegra_init_fuse_cache(void) { + unsigned i; + + kfuse_cache = kzalloc(KFUSE_CACHE_SZ, GFP_KERNEL); + + kfuse_clock_enable(1); + + kfuse_rewind(); + + printk(KERN_DEBUG "kfuse_cache_isvalid=%d\n", kfuse_cache_isvalid); + + if (!kfuse_cache_isvalid) { + printk(KERN_ERR "kfuse CRC or ECC error\n"); + } else { + /* load if no CRC or ECC errors */ + for (i = 0; i < KFUSE_CACHE_SZ / sizeof (u32); i ++) + kfuse_cache[i] = kfuse_read(); + } + + kfuse_clock_enable(0); +} + +const u32 *tegra_kfuse_cache_get(size_t *size) { + if (size) *size = KFUSE_CACHE_SZ; + return kfuse_cache; +} |