diff options
Diffstat (limited to 'arch/arm/mach-tegra/fuse.c')
| -rw-r--r-- | arch/arm/mach-tegra/fuse.c | 109 | 
1 files changed, 97 insertions, 12 deletions
diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index e035cd284a6..c9ac23b385b 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c @@ -21,14 +21,27 @@  #include <linux/kernel.h>  #include <linux/io.h>  #include <linux/export.h> +#include <linux/random.h> +#include <linux/clk.h>  #include <linux/tegra-soc.h>  #include "fuse.h"  #include "iomap.h"  #include "apbio.h" +/* Tegra20 only */  #define FUSE_UID_LOW		0x108  #define FUSE_UID_HIGH		0x10c + +/* Tegra30 and later */ +#define FUSE_VENDOR_CODE	0x200 +#define FUSE_FAB_CODE		0x204 +#define FUSE_LOT_CODE_0		0x208 +#define FUSE_LOT_CODE_1		0x20c +#define FUSE_WAFER_ID		0x210 +#define FUSE_X_COORDINATE	0x214 +#define FUSE_Y_COORDINATE	0x218 +  #define FUSE_SKU_INFO		0x110  #define TEGRA20_FUSE_SPARE_BIT		0x200 @@ -42,6 +55,7 @@ int tegra_cpu_speedo_id;		/* only exist in Tegra30 and later */  int tegra_soc_speedo_id;  enum tegra_revision tegra_revision; +static struct clk *fuse_clk;  static int tegra_fuse_spare_bit;  static void (*tegra_init_speedo_data)(void); @@ -65,6 +79,22 @@ static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {  	[TEGRA_REVISION_A04]     = "A04",  }; +static void tegra_fuse_enable_clk(void) +{ +	if (IS_ERR(fuse_clk)) +		fuse_clk = clk_get_sys(NULL, "fuse"); +	if (IS_ERR(fuse_clk)) +		return; +	clk_prepare_enable(fuse_clk); +} + +static void tegra_fuse_disable_clk(void) +{ +	if (IS_ERR(fuse_clk)) +		return; +	clk_disable_unprepare(fuse_clk); +} +  u32 tegra_fuse_readl(unsigned long offset)  {  	return tegra_apb_readl(TEGRA_FUSE_BASE + offset); @@ -72,7 +102,15 @@ u32 tegra_fuse_readl(unsigned long offset)  bool tegra_spare_fuse(int bit)  { -	return tegra_fuse_readl(tegra_fuse_spare_bit + bit * 4); +	bool ret; + +	tegra_fuse_enable_clk(); + +	ret = tegra_fuse_readl(tegra_fuse_spare_bit + bit * 4); + +	tegra_fuse_disable_clk(); + +	return ret;  }  static enum tegra_revision tegra_get_revision(u32 id) @@ -101,10 +139,14 @@ static void tegra_get_process_id(void)  {  	u32 reg; +	tegra_fuse_enable_clk(); +  	reg = tegra_fuse_readl(tegra_fuse_spare_bit);  	tegra_cpu_process_id = (reg >> 6) & 3;  	reg = tegra_fuse_readl(tegra_fuse_spare_bit);  	tegra_core_process_id = (reg >> 12) & 3; + +	tegra_fuse_disable_clk();  }  u32 tegra_read_chipid(void) @@ -112,21 +154,60 @@ u32 tegra_read_chipid(void)  	return readl_relaxed(IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804);  } -void tegra_init_fuse(void) +static void __init tegra20_fuse_init_randomness(void) +{ +	u32 randomness[2]; + +	randomness[0] = tegra_fuse_readl(FUSE_UID_LOW); +	randomness[1] = tegra_fuse_readl(FUSE_UID_HIGH); + +	add_device_randomness(randomness, sizeof(randomness)); +} + +/* Applies to Tegra30 or later */ +static void __init tegra30_fuse_init_randomness(void) +{ +	u32 randomness[7]; + +	randomness[0] = tegra_fuse_readl(FUSE_VENDOR_CODE); +	randomness[1] = tegra_fuse_readl(FUSE_FAB_CODE); +	randomness[2] = tegra_fuse_readl(FUSE_LOT_CODE_0); +	randomness[3] = tegra_fuse_readl(FUSE_LOT_CODE_1); +	randomness[4] = tegra_fuse_readl(FUSE_WAFER_ID); +	randomness[5] = tegra_fuse_readl(FUSE_X_COORDINATE); +	randomness[6] = tegra_fuse_readl(FUSE_Y_COORDINATE); + +	add_device_randomness(randomness, sizeof(randomness)); +} + +void __init tegra_init_fuse(void)  {  	u32 id; +	u32 randomness[5];  	u32 reg = readl(IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x48));  	reg |= 1 << 28;  	writel(reg, IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x48)); +	/* +	 * Enable FUSE clock. This needs to be hardcoded because the clock +	 * subsystem is not active during early boot. +	 */ +	reg = readl(IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x14)); +	reg |= 1 << 7; +	writel(reg, IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x14)); +	fuse_clk = ERR_PTR(-EINVAL); +  	reg = tegra_fuse_readl(FUSE_SKU_INFO); +	randomness[0] = reg;  	tegra_sku_id = reg & 0xFF;  	reg = tegra_apb_readl(TEGRA_APB_MISC_BASE + STRAP_OPT); +	randomness[1] = reg;  	tegra_bct_strapping = (reg & RAM_ID_MASK) >> RAM_CODE_SHIFT;  	id = tegra_read_chipid(); +	randomness[2] = id;  	tegra_chip_id = (id >> 8) & 0xff;  	switch (tegra_chip_id) { @@ -149,19 +230,23 @@ void tegra_init_fuse(void)  	tegra_revision = tegra_get_revision(id);  	tegra_init_speedo_data(); +	randomness[3] = (tegra_cpu_process_id << 16) | tegra_core_process_id; +	randomness[4] = (tegra_cpu_speedo_id << 16) | tegra_soc_speedo_id; + +	add_device_randomness(randomness, sizeof(randomness)); +	switch (tegra_chip_id) { +	case TEGRA20: +		tegra20_fuse_init_randomness(); +		break; +	case TEGRA30: +	case TEGRA114: +	default: +		tegra30_fuse_init_randomness(); +		break; +	}  	pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n",  		tegra_revision_name[tegra_revision],  		tegra_sku_id, tegra_cpu_process_id,  		tegra_core_process_id);  } - -unsigned long long tegra_chip_uid(void) -{ -	unsigned long long lo, hi; - -	lo = tegra_fuse_readl(FUSE_UID_LOW); -	hi = tegra_fuse_readl(FUSE_UID_HIGH); -	return (hi << 32ull) | lo; -} -EXPORT_SYMBOL(tegra_chip_uid);  | 
