1 /* 2 * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms and conditions of the GNU General Public License, 6 * version 2, as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 #include <linux/device.h> 18 #include <linux/kernel.h> 19 #include <linux/bug.h> 20 21 #include <soc/tegra/fuse.h> 22 23 #include "fuse.h" 24 25 #define CPU_PROCESS_CORNERS 2 26 #define GPU_PROCESS_CORNERS 2 27 #define SOC_PROCESS_CORNERS 3 28 29 #define FUSE_CPU_SPEEDO_0 0x014 30 #define FUSE_CPU_SPEEDO_1 0x02c 31 #define FUSE_CPU_SPEEDO_2 0x030 32 #define FUSE_SOC_SPEEDO_0 0x034 33 #define FUSE_SOC_SPEEDO_1 0x038 34 #define FUSE_SOC_SPEEDO_2 0x03c 35 #define FUSE_CPU_IDDQ 0x018 36 #define FUSE_SOC_IDDQ 0x040 37 #define FUSE_GPU_IDDQ 0x128 38 #define FUSE_FT_REV 0x028 39 40 enum { 41 THRESHOLD_INDEX_0, 42 THRESHOLD_INDEX_1, 43 THRESHOLD_INDEX_COUNT, 44 }; 45 46 static const u32 __initconst cpu_process_speedos[][CPU_PROCESS_CORNERS] = { 47 { 2119, UINT_MAX }, 48 { 2119, UINT_MAX }, 49 }; 50 51 static const u32 __initconst gpu_process_speedos[][GPU_PROCESS_CORNERS] = { 52 { UINT_MAX, UINT_MAX }, 53 { UINT_MAX, UINT_MAX }, 54 }; 55 56 static const u32 __initconst soc_process_speedos[][SOC_PROCESS_CORNERS] = { 57 { 1950, 2100, UINT_MAX }, 58 { 1950, 2100, UINT_MAX }, 59 }; 60 61 static u8 __init get_speedo_revision(void) 62 { 63 return tegra_fuse_read_spare(4) << 2 | 64 tegra_fuse_read_spare(3) << 1 | 65 tegra_fuse_read_spare(2) << 0; 66 } 67 68 static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info, 69 u8 speedo_rev, int *threshold) 70 { 71 int sku = sku_info->sku_id; 72 73 /* Assign to default */ 74 sku_info->cpu_speedo_id = 0; 75 sku_info->soc_speedo_id = 0; 76 sku_info->gpu_speedo_id = 0; 77 *threshold = THRESHOLD_INDEX_0; 78 79 switch (sku) { 80 case 0x00: /* Engineering SKU */ 81 case 0x01: /* Engineering SKU */ 82 case 0x07: 83 case 0x17: 84 case 0x27: 85 if (speedo_rev >= 2) 86 sku_info->gpu_speedo_id = 1; 87 break; 88 89 case 0x13: 90 if (speedo_rev >= 2) 91 sku_info->gpu_speedo_id = 1; 92 93 sku_info->cpu_speedo_id = 1; 94 break; 95 96 default: 97 pr_err("Tegra210: unknown SKU %#04x\n", sku); 98 /* Using the default for the error case */ 99 break; 100 } 101 } 102 103 static int get_process_id(int value, const u32 *speedos, unsigned int num) 104 { 105 unsigned int i; 106 107 for (i = 0; i < num; i++) 108 if (value < speedos[num]) 109 return i; 110 111 return -EINVAL; 112 } 113 114 void __init tegra210_init_speedo_data(struct tegra_sku_info *sku_info) 115 { 116 int cpu_speedo[3], soc_speedo[3], cpu_iddq, gpu_iddq, soc_iddq; 117 unsigned int index; 118 u8 speedo_revision; 119 120 BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != 121 THRESHOLD_INDEX_COUNT); 122 BUILD_BUG_ON(ARRAY_SIZE(gpu_process_speedos) != 123 THRESHOLD_INDEX_COUNT); 124 BUILD_BUG_ON(ARRAY_SIZE(soc_process_speedos) != 125 THRESHOLD_INDEX_COUNT); 126 127 /* Read speedo/IDDQ fuses */ 128 cpu_speedo[0] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_0); 129 cpu_speedo[1] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_1); 130 cpu_speedo[2] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_2); 131 132 soc_speedo[0] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_0); 133 soc_speedo[1] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_1); 134 soc_speedo[2] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_2); 135 136 cpu_iddq = tegra_fuse_read_early(FUSE_CPU_IDDQ) * 4; 137 soc_iddq = tegra_fuse_read_early(FUSE_SOC_IDDQ) * 4; 138 gpu_iddq = tegra_fuse_read_early(FUSE_GPU_IDDQ) * 5; 139 140 /* 141 * Determine CPU, GPU and SoC speedo values depending on speedo fusing 142 * revision. Note that GPU speedo value is fused in CPU_SPEEDO_2. 143 */ 144 speedo_revision = get_speedo_revision(); 145 pr_info("Speedo Revision %u\n", speedo_revision); 146 147 if (speedo_revision >= 3) { 148 sku_info->cpu_speedo_value = cpu_speedo[0]; 149 sku_info->gpu_speedo_value = cpu_speedo[2]; 150 sku_info->soc_speedo_value = soc_speedo[0]; 151 } else if (speedo_revision == 2) { 152 sku_info->cpu_speedo_value = (-1938 + (1095 * cpu_speedo[0] / 100)) / 10; 153 sku_info->gpu_speedo_value = (-1662 + (1082 * cpu_speedo[2] / 100)) / 10; 154 sku_info->soc_speedo_value = ( -705 + (1037 * soc_speedo[0] / 100)) / 10; 155 } else { 156 sku_info->cpu_speedo_value = 2100; 157 sku_info->gpu_speedo_value = cpu_speedo[2] - 75; 158 sku_info->soc_speedo_value = 1900; 159 } 160 161 if ((sku_info->cpu_speedo_value <= 0) || 162 (sku_info->gpu_speedo_value <= 0) || 163 (sku_info->soc_speedo_value <= 0)) { 164 WARN(1, "speedo value not fused\n"); 165 return; 166 } 167 168 rev_sku_to_speedo_ids(sku_info, speedo_revision, &index); 169 170 sku_info->gpu_process_id = get_process_id(sku_info->gpu_speedo_value, 171 gpu_process_speedos[index], 172 GPU_PROCESS_CORNERS); 173 174 sku_info->cpu_process_id = get_process_id(sku_info->cpu_speedo_value, 175 cpu_process_speedos[index], 176 CPU_PROCESS_CORNERS); 177 178 sku_info->soc_process_id = get_process_id(sku_info->soc_speedo_value, 179 soc_process_speedos[index], 180 SOC_PROCESS_CORNERS); 181 182 pr_debug("Tegra GPU Speedo ID=%d, Speedo Value=%d\n", 183 sku_info->gpu_speedo_id, sku_info->gpu_speedo_value); 184 } 185