// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2012-2013 * NVIDIA Corporation * * (C) Copyright 2022 * Svyatoslav Ryhel */ #include #include #include #include #include #include #include #include "cpu.h" #define FUSE_UID_LOW 0x108 #define FUSE_UID_HIGH 0x10c #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_VENDOR_CODE_MASK 0xf #define FUSE_FAB_CODE_MASK 0x3f #define FUSE_WAFER_ID_MASK 0x3f #define FUSE_X_COORDINATE_MASK 0x1ff #define FUSE_Y_COORDINATE_MASK 0x1ff static u32 tegra_fuse_readl(unsigned long offset) { return readl(NV_PA_FUSE_BASE + offset); } static void tegra_fuse_init(void) { u32 reg; /* * Performed by downstream and is not * documented by TRM. Whithout setting * this bit fuse region will not work. */ reg = readl_relaxed(NV_PA_CLK_RST_BASE + 0x48); reg |= BIT(28); writel(reg, NV_PA_CLK_RST_BASE + 0x48); clock_enable(PERIPH_ID_FUSE); udelay(2); reset_set_enable(PERIPH_ID_FUSE, 0); } unsigned long long tegra_chip_uid(void) { u64 uid = 0ull; u32 reg; u32 cid; u32 vendor; u32 fab; u32 lot; u32 wafer; u32 x; u32 y; u32 i; tegra_fuse_init(); /* 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 with the UID composed thusly: Where: Field Bits Position Data ------- ---- -------- ---------------------------------------- CID 4 60 Chip id 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 */ switch (tegra_get_chip()) { case CHIPID_TEGRA20: /* T20 has simple calculation */ return ((unsigned long long)tegra_fuse_readl(FUSE_UID_HIGH) << 32ull) | (unsigned long long)tegra_fuse_readl(FUSE_UID_LOW); case CHIPID_TEGRA30: /* T30 chip id is 0 */ cid = 0; break; case CHIPID_TEGRA114: /* T11x chip id is 1 */ cid = 1; break; case CHIPID_TEGRA124: /* T12x chip id is 3 */ cid = 3; break; case CHIPID_TEGRA210: /* T210 chip id is 5 */ cid = 5; default: return 0; } 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; 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; }