19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 20dc5a0d8SThierry Reding /* 30dc5a0d8SThierry Reding * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. 40dc5a0d8SThierry Reding */ 50dc5a0d8SThierry Reding 60dc5a0d8SThierry Reding #include <linux/device.h> 70dc5a0d8SThierry Reding #include <linux/kernel.h> 80dc5a0d8SThierry Reding #include <linux/bug.h> 90dc5a0d8SThierry Reding 100dc5a0d8SThierry Reding #include <soc/tegra/fuse.h> 110dc5a0d8SThierry Reding 120dc5a0d8SThierry Reding #include "fuse.h" 130dc5a0d8SThierry Reding 140dc5a0d8SThierry Reding #define CPU_PROCESS_CORNERS 2 150dc5a0d8SThierry Reding #define GPU_PROCESS_CORNERS 2 160dc5a0d8SThierry Reding #define SOC_PROCESS_CORNERS 3 170dc5a0d8SThierry Reding 180dc5a0d8SThierry Reding #define FUSE_CPU_SPEEDO_0 0x014 190dc5a0d8SThierry Reding #define FUSE_CPU_SPEEDO_1 0x02c 200dc5a0d8SThierry Reding #define FUSE_CPU_SPEEDO_2 0x030 210dc5a0d8SThierry Reding #define FUSE_SOC_SPEEDO_0 0x034 220dc5a0d8SThierry Reding #define FUSE_SOC_SPEEDO_1 0x038 230dc5a0d8SThierry Reding #define FUSE_SOC_SPEEDO_2 0x03c 240dc5a0d8SThierry Reding #define FUSE_CPU_IDDQ 0x018 250dc5a0d8SThierry Reding #define FUSE_SOC_IDDQ 0x040 260dc5a0d8SThierry Reding #define FUSE_GPU_IDDQ 0x128 270dc5a0d8SThierry Reding #define FUSE_FT_REV 0x028 280dc5a0d8SThierry Reding 290dc5a0d8SThierry Reding enum { 300dc5a0d8SThierry Reding THRESHOLD_INDEX_0, 310dc5a0d8SThierry Reding THRESHOLD_INDEX_1, 320dc5a0d8SThierry Reding THRESHOLD_INDEX_COUNT, 330dc5a0d8SThierry Reding }; 340dc5a0d8SThierry Reding 350dc5a0d8SThierry Reding static const u32 __initconst cpu_process_speedos[][CPU_PROCESS_CORNERS] = { 360dc5a0d8SThierry Reding { 2119, UINT_MAX }, 370dc5a0d8SThierry Reding { 2119, UINT_MAX }, 380dc5a0d8SThierry Reding }; 390dc5a0d8SThierry Reding 400dc5a0d8SThierry Reding static const u32 __initconst gpu_process_speedos[][GPU_PROCESS_CORNERS] = { 410dc5a0d8SThierry Reding { UINT_MAX, UINT_MAX }, 420dc5a0d8SThierry Reding { UINT_MAX, UINT_MAX }, 430dc5a0d8SThierry Reding }; 440dc5a0d8SThierry Reding 450dc5a0d8SThierry Reding static const u32 __initconst soc_process_speedos[][SOC_PROCESS_CORNERS] = { 460dc5a0d8SThierry Reding { 1950, 2100, UINT_MAX }, 470dc5a0d8SThierry Reding { 1950, 2100, UINT_MAX }, 480dc5a0d8SThierry Reding }; 490dc5a0d8SThierry Reding 500dc5a0d8SThierry Reding static u8 __init get_speedo_revision(void) 510dc5a0d8SThierry Reding { 520dc5a0d8SThierry Reding return tegra_fuse_read_spare(4) << 2 | 530dc5a0d8SThierry Reding tegra_fuse_read_spare(3) << 1 | 540dc5a0d8SThierry Reding tegra_fuse_read_spare(2) << 0; 550dc5a0d8SThierry Reding } 560dc5a0d8SThierry Reding 570dc5a0d8SThierry Reding static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info, 580dc5a0d8SThierry Reding u8 speedo_rev, int *threshold) 590dc5a0d8SThierry Reding { 600dc5a0d8SThierry Reding int sku = sku_info->sku_id; 610dc5a0d8SThierry Reding 620dc5a0d8SThierry Reding /* Assign to default */ 630dc5a0d8SThierry Reding sku_info->cpu_speedo_id = 0; 640dc5a0d8SThierry Reding sku_info->soc_speedo_id = 0; 650dc5a0d8SThierry Reding sku_info->gpu_speedo_id = 0; 660dc5a0d8SThierry Reding *threshold = THRESHOLD_INDEX_0; 670dc5a0d8SThierry Reding 680dc5a0d8SThierry Reding switch (sku) { 690dc5a0d8SThierry Reding case 0x00: /* Engineering SKU */ 700dc5a0d8SThierry Reding case 0x01: /* Engineering SKU */ 710dc5a0d8SThierry Reding case 0x07: 720dc5a0d8SThierry Reding case 0x17: 730dc5a0d8SThierry Reding case 0x27: 740dc5a0d8SThierry Reding if (speedo_rev >= 2) 750dc5a0d8SThierry Reding sku_info->gpu_speedo_id = 1; 760dc5a0d8SThierry Reding break; 770dc5a0d8SThierry Reding 780dc5a0d8SThierry Reding case 0x13: 790dc5a0d8SThierry Reding if (speedo_rev >= 2) 800dc5a0d8SThierry Reding sku_info->gpu_speedo_id = 1; 810dc5a0d8SThierry Reding 820dc5a0d8SThierry Reding sku_info->cpu_speedo_id = 1; 830dc5a0d8SThierry Reding break; 840dc5a0d8SThierry Reding 850dc5a0d8SThierry Reding default: 860dc5a0d8SThierry Reding pr_err("Tegra210: unknown SKU %#04x\n", sku); 870dc5a0d8SThierry Reding /* Using the default for the error case */ 880dc5a0d8SThierry Reding break; 890dc5a0d8SThierry Reding } 900dc5a0d8SThierry Reding } 910dc5a0d8SThierry Reding 920dc5a0d8SThierry Reding static int get_process_id(int value, const u32 *speedos, unsigned int num) 930dc5a0d8SThierry Reding { 940dc5a0d8SThierry Reding unsigned int i; 950dc5a0d8SThierry Reding 960dc5a0d8SThierry Reding for (i = 0; i < num; i++) 970dc5a0d8SThierry Reding if (value < speedos[num]) 980dc5a0d8SThierry Reding return i; 990dc5a0d8SThierry Reding 1000dc5a0d8SThierry Reding return -EINVAL; 1010dc5a0d8SThierry Reding } 1020dc5a0d8SThierry Reding 1030dc5a0d8SThierry Reding void __init tegra210_init_speedo_data(struct tegra_sku_info *sku_info) 1040dc5a0d8SThierry Reding { 105*6c37cb9fSLee Jones int cpu_speedo[3], soc_speedo[3]; 1060dc5a0d8SThierry Reding unsigned int index; 1070dc5a0d8SThierry Reding u8 speedo_revision; 1080dc5a0d8SThierry Reding 1090dc5a0d8SThierry Reding BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != 1100dc5a0d8SThierry Reding THRESHOLD_INDEX_COUNT); 1110dc5a0d8SThierry Reding BUILD_BUG_ON(ARRAY_SIZE(gpu_process_speedos) != 1120dc5a0d8SThierry Reding THRESHOLD_INDEX_COUNT); 1130dc5a0d8SThierry Reding BUILD_BUG_ON(ARRAY_SIZE(soc_process_speedos) != 1140dc5a0d8SThierry Reding THRESHOLD_INDEX_COUNT); 1150dc5a0d8SThierry Reding 1160dc5a0d8SThierry Reding /* Read speedo/IDDQ fuses */ 1170dc5a0d8SThierry Reding cpu_speedo[0] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_0); 1180dc5a0d8SThierry Reding cpu_speedo[1] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_1); 1190dc5a0d8SThierry Reding cpu_speedo[2] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_2); 1200dc5a0d8SThierry Reding 1210dc5a0d8SThierry Reding soc_speedo[0] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_0); 1220dc5a0d8SThierry Reding soc_speedo[1] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_1); 123d94da0ddSJoseph Lo soc_speedo[2] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_2); 1240dc5a0d8SThierry Reding 1250dc5a0d8SThierry Reding /* 1260dc5a0d8SThierry Reding * Determine CPU, GPU and SoC speedo values depending on speedo fusing 1270dc5a0d8SThierry Reding * revision. Note that GPU speedo value is fused in CPU_SPEEDO_2. 1280dc5a0d8SThierry Reding */ 1290dc5a0d8SThierry Reding speedo_revision = get_speedo_revision(); 1300dc5a0d8SThierry Reding pr_info("Speedo Revision %u\n", speedo_revision); 1310dc5a0d8SThierry Reding 1320dc5a0d8SThierry Reding if (speedo_revision >= 3) { 1330dc5a0d8SThierry Reding sku_info->cpu_speedo_value = cpu_speedo[0]; 1340dc5a0d8SThierry Reding sku_info->gpu_speedo_value = cpu_speedo[2]; 1350dc5a0d8SThierry Reding sku_info->soc_speedo_value = soc_speedo[0]; 1360dc5a0d8SThierry Reding } else if (speedo_revision == 2) { 1370dc5a0d8SThierry Reding sku_info->cpu_speedo_value = (-1938 + (1095 * cpu_speedo[0] / 100)) / 10; 1380dc5a0d8SThierry Reding sku_info->gpu_speedo_value = (-1662 + (1082 * cpu_speedo[2] / 100)) / 10; 1390dc5a0d8SThierry Reding sku_info->soc_speedo_value = ( -705 + (1037 * soc_speedo[0] / 100)) / 10; 1400dc5a0d8SThierry Reding } else { 1410dc5a0d8SThierry Reding sku_info->cpu_speedo_value = 2100; 1420dc5a0d8SThierry Reding sku_info->gpu_speedo_value = cpu_speedo[2] - 75; 1430dc5a0d8SThierry Reding sku_info->soc_speedo_value = 1900; 1440dc5a0d8SThierry Reding } 1450dc5a0d8SThierry Reding 1460dc5a0d8SThierry Reding if ((sku_info->cpu_speedo_value <= 0) || 1470dc5a0d8SThierry Reding (sku_info->gpu_speedo_value <= 0) || 1480dc5a0d8SThierry Reding (sku_info->soc_speedo_value <= 0)) { 1490dc5a0d8SThierry Reding WARN(1, "speedo value not fused\n"); 1500dc5a0d8SThierry Reding return; 1510dc5a0d8SThierry Reding } 1520dc5a0d8SThierry Reding 1530dc5a0d8SThierry Reding rev_sku_to_speedo_ids(sku_info, speedo_revision, &index); 1540dc5a0d8SThierry Reding 1550dc5a0d8SThierry Reding sku_info->gpu_process_id = get_process_id(sku_info->gpu_speedo_value, 1560dc5a0d8SThierry Reding gpu_process_speedos[index], 1570dc5a0d8SThierry Reding GPU_PROCESS_CORNERS); 1580dc5a0d8SThierry Reding 1590dc5a0d8SThierry Reding sku_info->cpu_process_id = get_process_id(sku_info->cpu_speedo_value, 1600dc5a0d8SThierry Reding cpu_process_speedos[index], 1610dc5a0d8SThierry Reding CPU_PROCESS_CORNERS); 1620dc5a0d8SThierry Reding 1630dc5a0d8SThierry Reding sku_info->soc_process_id = get_process_id(sku_info->soc_speedo_value, 1640dc5a0d8SThierry Reding soc_process_speedos[index], 1650dc5a0d8SThierry Reding SOC_PROCESS_CORNERS); 1660dc5a0d8SThierry Reding 1670dc5a0d8SThierry Reding pr_debug("Tegra GPU Speedo ID=%d, Speedo Value=%d\n", 1680dc5a0d8SThierry Reding sku_info->gpu_speedo_id, sku_info->gpu_speedo_value); 1690dc5a0d8SThierry Reding } 170