10dc5a0d8SThierry Reding /* 20dc5a0d8SThierry Reding * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. 30dc5a0d8SThierry Reding * 40dc5a0d8SThierry Reding * This program is free software; you can redistribute it and/or modify it 50dc5a0d8SThierry Reding * under the terms and conditions of the GNU General Public License, 60dc5a0d8SThierry Reding * version 2, as published by the Free Software Foundation. 70dc5a0d8SThierry Reding * 80dc5a0d8SThierry Reding * This program is distributed in the hope it will be useful, but WITHOUT 90dc5a0d8SThierry Reding * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 100dc5a0d8SThierry Reding * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 110dc5a0d8SThierry Reding * more details. 120dc5a0d8SThierry Reding * 130dc5a0d8SThierry Reding * You should have received a copy of the GNU General Public License 140dc5a0d8SThierry Reding * along with this program. If not, see <http://www.gnu.org/licenses/>. 150dc5a0d8SThierry Reding */ 160dc5a0d8SThierry Reding 170dc5a0d8SThierry Reding #include <linux/device.h> 180dc5a0d8SThierry Reding #include <linux/kernel.h> 190dc5a0d8SThierry Reding #include <linux/bug.h> 200dc5a0d8SThierry Reding 210dc5a0d8SThierry Reding #include <soc/tegra/fuse.h> 220dc5a0d8SThierry Reding 230dc5a0d8SThierry Reding #include "fuse.h" 240dc5a0d8SThierry Reding 250dc5a0d8SThierry Reding #define CPU_PROCESS_CORNERS 2 260dc5a0d8SThierry Reding #define GPU_PROCESS_CORNERS 2 270dc5a0d8SThierry Reding #define SOC_PROCESS_CORNERS 3 280dc5a0d8SThierry Reding 290dc5a0d8SThierry Reding #define FUSE_CPU_SPEEDO_0 0x014 300dc5a0d8SThierry Reding #define FUSE_CPU_SPEEDO_1 0x02c 310dc5a0d8SThierry Reding #define FUSE_CPU_SPEEDO_2 0x030 320dc5a0d8SThierry Reding #define FUSE_SOC_SPEEDO_0 0x034 330dc5a0d8SThierry Reding #define FUSE_SOC_SPEEDO_1 0x038 340dc5a0d8SThierry Reding #define FUSE_SOC_SPEEDO_2 0x03c 350dc5a0d8SThierry Reding #define FUSE_CPU_IDDQ 0x018 360dc5a0d8SThierry Reding #define FUSE_SOC_IDDQ 0x040 370dc5a0d8SThierry Reding #define FUSE_GPU_IDDQ 0x128 380dc5a0d8SThierry Reding #define FUSE_FT_REV 0x028 390dc5a0d8SThierry Reding 400dc5a0d8SThierry Reding enum { 410dc5a0d8SThierry Reding THRESHOLD_INDEX_0, 420dc5a0d8SThierry Reding THRESHOLD_INDEX_1, 430dc5a0d8SThierry Reding THRESHOLD_INDEX_COUNT, 440dc5a0d8SThierry Reding }; 450dc5a0d8SThierry Reding 460dc5a0d8SThierry Reding static const u32 __initconst cpu_process_speedos[][CPU_PROCESS_CORNERS] = { 470dc5a0d8SThierry Reding { 2119, UINT_MAX }, 480dc5a0d8SThierry Reding { 2119, UINT_MAX }, 490dc5a0d8SThierry Reding }; 500dc5a0d8SThierry Reding 510dc5a0d8SThierry Reding static const u32 __initconst gpu_process_speedos[][GPU_PROCESS_CORNERS] = { 520dc5a0d8SThierry Reding { UINT_MAX, UINT_MAX }, 530dc5a0d8SThierry Reding { UINT_MAX, UINT_MAX }, 540dc5a0d8SThierry Reding }; 550dc5a0d8SThierry Reding 560dc5a0d8SThierry Reding static const u32 __initconst soc_process_speedos[][SOC_PROCESS_CORNERS] = { 570dc5a0d8SThierry Reding { 1950, 2100, UINT_MAX }, 580dc5a0d8SThierry Reding { 1950, 2100, UINT_MAX }, 590dc5a0d8SThierry Reding }; 600dc5a0d8SThierry Reding 610dc5a0d8SThierry Reding static u8 __init get_speedo_revision(void) 620dc5a0d8SThierry Reding { 630dc5a0d8SThierry Reding return tegra_fuse_read_spare(4) << 2 | 640dc5a0d8SThierry Reding tegra_fuse_read_spare(3) << 1 | 650dc5a0d8SThierry Reding tegra_fuse_read_spare(2) << 0; 660dc5a0d8SThierry Reding } 670dc5a0d8SThierry Reding 680dc5a0d8SThierry Reding static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info, 690dc5a0d8SThierry Reding u8 speedo_rev, int *threshold) 700dc5a0d8SThierry Reding { 710dc5a0d8SThierry Reding int sku = sku_info->sku_id; 720dc5a0d8SThierry Reding 730dc5a0d8SThierry Reding /* Assign to default */ 740dc5a0d8SThierry Reding sku_info->cpu_speedo_id = 0; 750dc5a0d8SThierry Reding sku_info->soc_speedo_id = 0; 760dc5a0d8SThierry Reding sku_info->gpu_speedo_id = 0; 770dc5a0d8SThierry Reding *threshold = THRESHOLD_INDEX_0; 780dc5a0d8SThierry Reding 790dc5a0d8SThierry Reding switch (sku) { 800dc5a0d8SThierry Reding case 0x00: /* Engineering SKU */ 810dc5a0d8SThierry Reding case 0x01: /* Engineering SKU */ 820dc5a0d8SThierry Reding case 0x07: 830dc5a0d8SThierry Reding case 0x17: 840dc5a0d8SThierry Reding case 0x27: 850dc5a0d8SThierry Reding if (speedo_rev >= 2) 860dc5a0d8SThierry Reding sku_info->gpu_speedo_id = 1; 870dc5a0d8SThierry Reding break; 880dc5a0d8SThierry Reding 890dc5a0d8SThierry Reding case 0x13: 900dc5a0d8SThierry Reding if (speedo_rev >= 2) 910dc5a0d8SThierry Reding sku_info->gpu_speedo_id = 1; 920dc5a0d8SThierry Reding 930dc5a0d8SThierry Reding sku_info->cpu_speedo_id = 1; 940dc5a0d8SThierry Reding break; 950dc5a0d8SThierry Reding 960dc5a0d8SThierry Reding default: 970dc5a0d8SThierry Reding pr_err("Tegra210: unknown SKU %#04x\n", sku); 980dc5a0d8SThierry Reding /* Using the default for the error case */ 990dc5a0d8SThierry Reding break; 1000dc5a0d8SThierry Reding } 1010dc5a0d8SThierry Reding } 1020dc5a0d8SThierry Reding 1030dc5a0d8SThierry Reding static int get_process_id(int value, const u32 *speedos, unsigned int num) 1040dc5a0d8SThierry Reding { 1050dc5a0d8SThierry Reding unsigned int i; 1060dc5a0d8SThierry Reding 1070dc5a0d8SThierry Reding for (i = 0; i < num; i++) 1080dc5a0d8SThierry Reding if (value < speedos[num]) 1090dc5a0d8SThierry Reding return i; 1100dc5a0d8SThierry Reding 1110dc5a0d8SThierry Reding return -EINVAL; 1120dc5a0d8SThierry Reding } 1130dc5a0d8SThierry Reding 1140dc5a0d8SThierry Reding void __init tegra210_init_speedo_data(struct tegra_sku_info *sku_info) 1150dc5a0d8SThierry Reding { 1160dc5a0d8SThierry Reding int cpu_speedo[3], soc_speedo[3], cpu_iddq, gpu_iddq, soc_iddq; 1170dc5a0d8SThierry Reding unsigned int index; 1180dc5a0d8SThierry Reding u8 speedo_revision; 1190dc5a0d8SThierry Reding 1200dc5a0d8SThierry Reding BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != 1210dc5a0d8SThierry Reding THRESHOLD_INDEX_COUNT); 1220dc5a0d8SThierry Reding BUILD_BUG_ON(ARRAY_SIZE(gpu_process_speedos) != 1230dc5a0d8SThierry Reding THRESHOLD_INDEX_COUNT); 1240dc5a0d8SThierry Reding BUILD_BUG_ON(ARRAY_SIZE(soc_process_speedos) != 1250dc5a0d8SThierry Reding THRESHOLD_INDEX_COUNT); 1260dc5a0d8SThierry Reding 1270dc5a0d8SThierry Reding /* Read speedo/IDDQ fuses */ 1280dc5a0d8SThierry Reding cpu_speedo[0] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_0); 1290dc5a0d8SThierry Reding cpu_speedo[1] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_1); 1300dc5a0d8SThierry Reding cpu_speedo[2] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_2); 1310dc5a0d8SThierry Reding 1320dc5a0d8SThierry Reding soc_speedo[0] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_0); 1330dc5a0d8SThierry Reding soc_speedo[1] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_1); 134*d94da0ddSJoseph Lo soc_speedo[2] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_2); 1350dc5a0d8SThierry Reding 1360dc5a0d8SThierry Reding cpu_iddq = tegra_fuse_read_early(FUSE_CPU_IDDQ) * 4; 1370dc5a0d8SThierry Reding soc_iddq = tegra_fuse_read_early(FUSE_SOC_IDDQ) * 4; 1380dc5a0d8SThierry Reding gpu_iddq = tegra_fuse_read_early(FUSE_GPU_IDDQ) * 5; 1390dc5a0d8SThierry Reding 1400dc5a0d8SThierry Reding /* 1410dc5a0d8SThierry Reding * Determine CPU, GPU and SoC speedo values depending on speedo fusing 1420dc5a0d8SThierry Reding * revision. Note that GPU speedo value is fused in CPU_SPEEDO_2. 1430dc5a0d8SThierry Reding */ 1440dc5a0d8SThierry Reding speedo_revision = get_speedo_revision(); 1450dc5a0d8SThierry Reding pr_info("Speedo Revision %u\n", speedo_revision); 1460dc5a0d8SThierry Reding 1470dc5a0d8SThierry Reding if (speedo_revision >= 3) { 1480dc5a0d8SThierry Reding sku_info->cpu_speedo_value = cpu_speedo[0]; 1490dc5a0d8SThierry Reding sku_info->gpu_speedo_value = cpu_speedo[2]; 1500dc5a0d8SThierry Reding sku_info->soc_speedo_value = soc_speedo[0]; 1510dc5a0d8SThierry Reding } else if (speedo_revision == 2) { 1520dc5a0d8SThierry Reding sku_info->cpu_speedo_value = (-1938 + (1095 * cpu_speedo[0] / 100)) / 10; 1530dc5a0d8SThierry Reding sku_info->gpu_speedo_value = (-1662 + (1082 * cpu_speedo[2] / 100)) / 10; 1540dc5a0d8SThierry Reding sku_info->soc_speedo_value = ( -705 + (1037 * soc_speedo[0] / 100)) / 10; 1550dc5a0d8SThierry Reding } else { 1560dc5a0d8SThierry Reding sku_info->cpu_speedo_value = 2100; 1570dc5a0d8SThierry Reding sku_info->gpu_speedo_value = cpu_speedo[2] - 75; 1580dc5a0d8SThierry Reding sku_info->soc_speedo_value = 1900; 1590dc5a0d8SThierry Reding } 1600dc5a0d8SThierry Reding 1610dc5a0d8SThierry Reding if ((sku_info->cpu_speedo_value <= 0) || 1620dc5a0d8SThierry Reding (sku_info->gpu_speedo_value <= 0) || 1630dc5a0d8SThierry Reding (sku_info->soc_speedo_value <= 0)) { 1640dc5a0d8SThierry Reding WARN(1, "speedo value not fused\n"); 1650dc5a0d8SThierry Reding return; 1660dc5a0d8SThierry Reding } 1670dc5a0d8SThierry Reding 1680dc5a0d8SThierry Reding rev_sku_to_speedo_ids(sku_info, speedo_revision, &index); 1690dc5a0d8SThierry Reding 1700dc5a0d8SThierry Reding sku_info->gpu_process_id = get_process_id(sku_info->gpu_speedo_value, 1710dc5a0d8SThierry Reding gpu_process_speedos[index], 1720dc5a0d8SThierry Reding GPU_PROCESS_CORNERS); 1730dc5a0d8SThierry Reding 1740dc5a0d8SThierry Reding sku_info->cpu_process_id = get_process_id(sku_info->cpu_speedo_value, 1750dc5a0d8SThierry Reding cpu_process_speedos[index], 1760dc5a0d8SThierry Reding CPU_PROCESS_CORNERS); 1770dc5a0d8SThierry Reding 1780dc5a0d8SThierry Reding sku_info->soc_process_id = get_process_id(sku_info->soc_speedo_value, 1790dc5a0d8SThierry Reding soc_process_speedos[index], 1800dc5a0d8SThierry Reding SOC_PROCESS_CORNERS); 1810dc5a0d8SThierry Reding 1820dc5a0d8SThierry Reding pr_debug("Tegra GPU Speedo ID=%d, Speedo Value=%d\n", 1830dc5a0d8SThierry Reding sku_info->gpu_speedo_id, sku_info->gpu_speedo_value); 1840dc5a0d8SThierry Reding } 185