Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | // SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2012-2013 * NVIDIA Corporation <www.nvidia.com> * * (C) Copyright 2022 * Svyatoslav Ryhel <clamor95@gmail.com> */ #include <linux/delay.h> #include <asm/io.h> #include <asm/arch/tegra.h> #include <asm/arch/gp_padctrl.h> #include <asm/arch/clock.h> #include <asm/arch-tegra/fuse.h> #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); } 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); if (reg & BIT(28)) return; writel(reg | BIT(28), 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 <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 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; } static int tegra_is_production_mode_fuse_set(struct fuse_regs *fuse) { return readl(&fuse->production_mode); } static int tegra_is_odm_production_mode_fuse_set(struct fuse_regs *fuse) { return readl(&fuse->security_mode); } static int tegra_is_failure_analysis_mode(struct fuse_regs *fuse) { return readl(&fuse->fa); } static int tegra_is_sbk_zeroes(struct fuse_regs *fuse) { int i; for (i = 0; i < 4; i++) if (readl(&fuse->sbk[i])) return 0; return 1; } static int tegra_is_production_mode(struct fuse_regs *fuse) { if (!tegra_get_major_version()) return 1; return !tegra_is_failure_analysis_mode(fuse) && tegra_is_production_mode_fuse_set(fuse); } enum fuse_operating_mode tegra_fuse_get_operation_mode(void) { struct fuse_regs *fuse = (struct fuse_regs *)NV_PA_FUSE_BASE; tegra_fuse_init(); if (tegra_is_production_mode(fuse)) { if (!tegra_is_odm_production_mode_fuse_set(fuse)) return MODE_PRODUCTION; else if (tegra_is_sbk_zeroes(fuse)) return MODE_ODM_PRODUCTION_OPEN; else return MODE_ODM_PRODUCTION_SECURE; } return MODE_UNDEFINED; } |