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