diff options
author | Preetham Chandru R <pchandru@nvidia.com> | 2012-10-10 15:37:28 +0530 |
---|---|---|
committer | Matthew Pedro <mapedro@nvidia.com> | 2012-10-12 13:10:11 -0700 |
commit | b0935d3914c6a01349eaacce08d28620e7b78f6f (patch) | |
tree | 3054561817f02cceadef7e365d36d365e8988b91 /arch/arm/mach-tegra/tegra_fuse.c | |
parent | 1a2a5beb5de03cb58e4a37fa72193a8e662e9b22 (diff) |
ARM: tegra: rename fuse driver
The fuse driver registers parameters and thus
create a /sys/module/fuse directory which collides
with the fuse filesystem driver.
Rename fuse.c to tegra-fuse.c to fix this.
Bug 1058350
Change-Id: I5201bc43fcb44df8181085322bc40d1f6e99bf93
Signed-off-by: Marc Dietrich <marvin24@gmx.de>
Signed-off-by: Preetham Chandru R <pchandru@nvidia.com>
Reviewed-on: http://git-master/r/143206
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Stephen Warren <swarren@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Dan Willemsen <dwillemsen@nvidia.com>
Reviewed-by: Matthew Pedro <mapedro@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra_fuse.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra_fuse.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra_fuse.c b/arch/arm/mach-tegra/tegra_fuse.c new file mode 100644 index 000000000000..11328c4cada3 --- /dev/null +++ b/arch/arm/mach-tegra/tegra_fuse.c @@ -0,0 +1,461 @@ +/* + * arch/arm/mach-tegra/tegra_fuse.c + * + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2010-2012 NVIDIA Corp. + * + * Author: + * Colin Cross <ccross@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/module.h> +#include <linux/moduleparam.h> + +#include <mach/iomap.h> +#include <mach/tegra_fuse.h> + +#include "fuse.h" +#include "apbio.h" + +#define FUSE_SKU_INFO 0x110 +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) +#define FUSE_UID_LOW 0x108 +#define FUSE_UID_HIGH 0x10c +#define FUSE_SPARE_BIT 0x200 +#else +#define FUSE_VENDOR_CODE 0x200 +#define FUSE_VENDOR_CODE_MASK 0xf +#define FUSE_FAB_CODE 0x204 +#define FUSE_FAB_CODE_MASK 0x3f +#define FUSE_LOT_CODE_0 0x208 +#define FUSE_LOT_CODE_1 0x20c +#define FUSE_WAFER_ID 0x210 +#define FUSE_WAFER_ID_MASK 0x3f +#define FUSE_X_COORDINATE 0x214 +#define FUSE_X_COORDINATE_MASK 0x1ff +#define FUSE_Y_COORDINATE 0x218 +#define FUSE_Y_COORDINATE_MASK 0x1ff +#define FUSE_GPU_INFO 0x390 +#define FUSE_GPU_INFO_MASK (1<<2) +#define FUSE_SPARE_BIT 0x244 +/* fuse registers used in public fuse data read API */ +#define FUSE_TEST_PROGRAM_REVISION_0 0x128 +/* fuse spare bits are used to get Tj-ADT values */ +#define FUSE_SPARE_BIT_0_0 0x244 +#define NUM_TSENSOR_SPARE_BITS 28 +/* tsensor calibration register */ +#define FUSE_TSENSOR_CALIB_0 0x198 + +#endif + +#define TEGRA_AGE_0_6 0x2cc /*Spare bit 34*/ +#define TEGRA_AGE_1_6 0x308 /*Spare bit 49*/ +#define TEGRA_AGE_0_5 0x2c8 /*Spare bit 33*/ +#define TEGRA_AGE_1_5 0x304 /*Spare bit 48*/ +#define TEGRA_AGE_0_4 0x2c4 /*Spare bit 32*/ +#define TEGRA_AGE_1_4 0x300 /*Spare bit 47*/ +#define TEGRA_AGE_0_3 0x2c0 /*Spare bit 31*/ +#define TEGRA_AGE_1_3 0x2fc /*Spare bit 46*/ +#define TEGRA_AGE_0_2 0x2bc /*Spare bit 30*/ +#define TEGRA_AGE_1_2 0x2f8 /*Spare bit 45*/ +#define TEGRA_AGE_0_1 0x2b8 /*Spare bit 29*/ +#define TEGRA_AGE_1_1 0x2f4 /*Spare bit 44*/ +#define TEGRA_AGE_0_0 0x2b4 /*Spare bit 28*/ +#define TEGRA_AGE_1_0 0x2f0 /*Spare bit 43*/ + +struct tegra_id { + enum tegra_chipid chipid; + unsigned int major, minor, netlist, patch; + enum tegra_revision revision; + char *priv; +}; + +static struct tegra_id tegra_id; + +static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { + [TEGRA_REVISION_UNKNOWN] = "unknown", + [TEGRA_REVISION_A01] = "A01", + [TEGRA_REVISION_A02] = "A02", + [TEGRA_REVISION_A03] = "A03", + [TEGRA_REVISION_A03p] = "A03 prime", + [TEGRA_REVISION_A04] = "A04", + [TEGRA_REVISION_A04p] = "A04 prime", +}; + +u32 tegra_fuse_readl(unsigned long offset) +{ + return tegra_apb_readl(TEGRA_FUSE_BASE + offset); +} + +void tegra_fuse_writel(u32 value, unsigned long offset) +{ + tegra_apb_writel(value, TEGRA_FUSE_BASE + offset); +} + +static inline bool get_spare_fuse(int bit) +{ + return tegra_fuse_readl(FUSE_SPARE_BIT + bit * 4); +} + +const char *tegra_get_revision_name(void) +{ + return tegra_revision_name[tegra_get_revision()]; +} + +void tegra_init_fuse(void) +{ + u32 reg = readl(IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48)); + reg |= 1 << 28; + writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48)); + tegra_init_speedo_data(); + + pr_info("Tegra Revision: %s " + "SKU: 0x%x CPU Process: %d Core Process: %d\n", + tegra_get_revision_name(), tegra_sku_id(), + tegra_cpu_process_id(), tegra_core_process_id()); +} + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +int tegra_fuse_get_revision(u32 *rev) +{ + return -ENOENT; +} +EXPORT_SYMBOL(tegra_fuse_get_revision); + +int tegra_fuse_get_tsensor_calibration_data(u32 *calib) +{ + return -ENOENT; +} +EXPORT_SYMBOL(tegra_fuse_get_tsensor_calibration_data); + +int tegra_fuse_get_tsensor_spare_bits(u32 *spare_bits) +{ + return -ENOENT; +} +EXPORT_SYMBOL(tegra_fuse_get_tsensor_spare_bits); + +#else + +int tegra_fuse_get_revision(u32 *rev) +{ + /* fuse revision */ + *rev = tegra_fuse_readl(FUSE_TEST_PROGRAM_REVISION_0); + return 0; +} +EXPORT_SYMBOL(tegra_fuse_get_revision); + +int tegra_fuse_get_tsensor_calibration_data(u32 *calib) +{ + /* tsensor calibration fuse */ + *calib = tegra_fuse_readl(FUSE_TSENSOR_CALIB_0); + return 0; +} +EXPORT_SYMBOL(tegra_fuse_get_tsensor_calibration_data); + +int tegra_fuse_get_tsensor_spare_bits(u32 *spare_bits) +{ + u32 value; + int i; + + BUG_ON(NUM_TSENSOR_SPARE_BITS > (sizeof(u32) * 8)); + if (!spare_bits) + return -ENOMEM; + *spare_bits = 0; + /* spare bits 0-27 */ + for (i = 0; i < NUM_TSENSOR_SPARE_BITS; i++) { + value = tegra_fuse_readl(FUSE_SPARE_BIT_0_0 + + (i << 2)); + if (value) + *spare_bits |= BIT(i); + } + return 0; +} +EXPORT_SYMBOL(tegra_fuse_get_tsensor_spare_bits); +#endif + +#define TEGRA_READ_AGE_BIT(n, bit, age) {\ + bit = tegra_fuse_readl(TEGRA_AGE_0_##n);\ + bit |= tegra_fuse_readl(TEGRA_AGE_1_##n);\ + bit = bit << n;\ + age |= bit;\ +} + +int tegra_get_age(void) +{ + int linear_age, age_bit; + linear_age = age_bit = 0; + + TEGRA_READ_AGE_BIT(6, age_bit, linear_age); + TEGRA_READ_AGE_BIT(5, age_bit, linear_age); + TEGRA_READ_AGE_BIT(4, age_bit, linear_age); + TEGRA_READ_AGE_BIT(3, age_bit, linear_age); + TEGRA_READ_AGE_BIT(2, age_bit, linear_age); + TEGRA_READ_AGE_BIT(1, age_bit, linear_age); + TEGRA_READ_AGE_BIT(0, age_bit, linear_age); + + /*Default Aug, 2012*/ + if (linear_age <= 0) + linear_age = 8; + + pr_info("TEGRA: Linear age: %d\n", linear_age); + + return linear_age; +} + +unsigned long long tegra_chip_uid(void) +{ +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) + unsigned long long lo, hi; + + lo = tegra_fuse_readl(FUSE_UID_LOW); + hi = tegra_fuse_readl(FUSE_UID_HIGH); + return (hi << 32ull) | lo; +#else + u64 uid = 0ull; + u32 reg; + u32 cid; + u32 vendor; + u32 fab; + u32 lot; + u32 wafer; + u32 x; + u32 y; + u32 i; + + /* This used to be so much easier in prior chips. Unfortunately, there + is no one-stop shopping for the unique id anymore. It must be + constructed from various bits of information burned into the fuses + during the manufacturing process. The 64-bit unique id is formed + by concatenating several bit fields. The notation used for the + various fields is <fieldname:size_in_bits> with the UID composed + thusly: + + <CID:4><VENDOR:4><FAB:6><LOT:26><WAFER:6><X:9><Y:9> + + Where: + + Field Bits Position Data + ------- ---- -------- ---------------------------------------- + CID 4 60 Chip id (encoded as zero for T30) + VENDOR 4 56 Vendor code + FAB 6 50 FAB code + LOT 26 24 Lot code (5-digit base-36-coded-decimal, + re-encoded to 26 bits binary) + WAFER 6 18 Wafer id + X 9 9 Wafer X-coordinate + Y 9 0 Wafer Y-coordinate + ------- ---- + Total 64 + */ + + /* Get the chip id and encode each chip variant as a unique value. */ + reg = readl(IO_TO_VIRT(TEGRA_APB_MISC_BASE + 0x804)); + reg = (reg & 0xFF00) >> 8; + + switch (reg) { + case TEGRA_CHIPID_TEGRA3: + cid = 0; + break; + + default: + BUG(); + break; + } + + vendor = tegra_fuse_readl(FUSE_VENDOR_CODE) & FUSE_VENDOR_CODE_MASK; + fab = tegra_fuse_readl(FUSE_FAB_CODE) & FUSE_FAB_CODE_MASK; + + /* Lot code must be re-encoded from a 5 digit base-36 'BCD' number + to a binary number. */ + lot = 0; + reg = tegra_fuse_readl(FUSE_LOT_CODE_0) << 2; + + for (i = 0; i < 5; ++i) { + u32 digit = (reg & 0xFC000000) >> 26; + BUG_ON(digit >= 36); + lot *= 36; + lot += digit; + reg <<= 6; + } + + wafer = tegra_fuse_readl(FUSE_WAFER_ID) & FUSE_WAFER_ID_MASK; + x = tegra_fuse_readl(FUSE_X_COORDINATE) & FUSE_X_COORDINATE_MASK; + y = tegra_fuse_readl(FUSE_Y_COORDINATE) & FUSE_Y_COORDINATE_MASK; + + uid = ((unsigned long long)cid << 60ull) + | ((unsigned long long)vendor << 56ull) + | ((unsigned long long)fab << 50ull) + | ((unsigned long long)lot << 24ull) + | ((unsigned long long)wafer << 18ull) + | ((unsigned long long)x << 9ull) + | ((unsigned long long)y << 0ull); + return uid; +#endif +} + +unsigned int tegra_spare_fuse(int bit) +{ + BUG_ON(bit < 0 || bit > 61); + return tegra_fuse_readl(FUSE_SPARE_BIT + bit * 4); +} + +int tegra_sku_id(void) +{ + static int sku_id = -1; + if (sku_id == -1) { + u32 reg = tegra_fuse_readl(FUSE_SKU_INFO); + sku_id = reg & 0xFF; + } + return sku_id; +} + +int tegra_gpu_register_sets(void) +{ +#ifdef CONFIG_ARCH_TEGRA_HAS_DUAL_3D + u32 reg = readl(IO_TO_VIRT(TEGRA_CLK_RESET_BASE + FUSE_GPU_INFO)); + if (reg & FUSE_GPU_INFO_MASK) + return 1; + else + return 2; +#else + return 1; +#endif +} + +struct chip_revision { + enum tegra_chipid chipid; + unsigned int major; + unsigned int minor; + char prime; + enum tegra_revision revision; +}; + +#define CHIP_REVISION(id, m, n, p, rev) { \ + .chipid = TEGRA_CHIPID_##id, \ + .major = m, \ + .minor = n, \ + .prime = p, \ + .revision = TEGRA_REVISION_##rev } + +static struct chip_revision tegra_chip_revisions[] = { + CHIP_REVISION(TEGRA2, 1, 2, 0, A02), + CHIP_REVISION(TEGRA2, 1, 3, 0, A03), + CHIP_REVISION(TEGRA2, 1, 3, 'p', A03p), + CHIP_REVISION(TEGRA2, 1, 4, 0, A04), + CHIP_REVISION(TEGRA2, 1, 4, 'p', A04p), + CHIP_REVISION(TEGRA3, 1, 1, 0, A01), + CHIP_REVISION(TEGRA3, 1, 2, 0, A02), + CHIP_REVISION(TEGRA3, 1, 3, 0, A03), +}; + +static enum tegra_revision tegra_decode_revision(const struct tegra_id *id) +{ + enum tegra_revision revision = TEGRA_REVISION_UNKNOWN; + +#if defined(CONFIG_TEGRA_SILICON_PLATFORM) + int i ; + char prime; + + if (id->priv == NULL) + prime = 0; + else + prime = *(id->priv); + + for (i = 0; i < ARRAY_SIZE(tegra_chip_revisions); i++) { + if ((id->chipid != tegra_chip_revisions[i].chipid) || + (id->minor != tegra_chip_revisions[i].minor) || + (id->major != tegra_chip_revisions[i].major) || + (prime != tegra_chip_revisions[i].prime)) + continue; + + revision = tegra_chip_revisions[i].revision; + break; + } +#endif + + return revision; +} + +static void tegra_set_tegraid(u32 chipid, + u32 major, u32 minor, + u32 nlist, u32 patch, const char *priv) +{ + tegra_id.chipid = (enum tegra_chipid) chipid; + tegra_id.major = major; + tegra_id.minor = minor; + tegra_id.netlist = nlist; + tegra_id.patch = patch; + tegra_id.priv = (char *)priv; + tegra_id.revision = tegra_decode_revision(&tegra_id); +} + +static void tegra_get_tegraid_from_hw(void) +{ + void __iomem *chip_id = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804; + void __iomem *netlist = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x860; + u32 cid = readl(chip_id); + u32 nlist = readl(netlist); + char *priv = NULL; + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + if (get_spare_fuse(18) || get_spare_fuse(19)) + priv = "p"; +#endif + tegra_set_tegraid((cid >> 8) & 0xff, + (cid >> 4) & 0xf, + (cid >> 16) & 0xf, + (nlist >> 0) & 0xffff, + (nlist >> 16) & 0xffff, + priv); +} + +enum tegra_chipid tegra_get_chipid(void) +{ + if (tegra_id.chipid == TEGRA_CHIPID_UNKNOWN) + tegra_get_tegraid_from_hw(); + + return tegra_id.chipid; +} + +enum tegra_revision tegra_get_revision(void) +{ + if (tegra_id.chipid == TEGRA_CHIPID_UNKNOWN) + tegra_get_tegraid_from_hw(); + + return tegra_id.revision; +} + +static int get_chip_id(char *val, const struct kernel_param *kp) +{ + return param_get_uint(val, kp); +} + +static int get_revision(char *val, const struct kernel_param *kp) +{ + return param_get_uint(val, kp); +} + +static struct kernel_param_ops tegra_chip_id_ops = { + .get = get_chip_id, +}; + +static struct kernel_param_ops tegra_revision_ops = { + .get = get_revision, +}; + +module_param_cb(tegra_chip_id, &tegra_chip_id_ops, &tegra_id.chipid, 0444); +module_param_cb(tegra_chip_rev, &tegra_revision_ops, &tegra_id.revision, 0444); |