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