]>
Commit | Line | Data |
---|---|---|
43c50873 PF |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright 2019 NXP | |
4 | */ | |
5 | ||
43c50873 PF |
6 | #include <cpu.h> |
7 | #include <dm.h> | |
8 | #include <thermal.h> | |
401d1c4f | 9 | #include <asm/global_data.h> |
90526e9f | 10 | #include <asm/system.h> |
99ac6c76 | 11 | #include <firmware/imx/sci/sci.h> |
43c50873 PF |
12 | #include <asm/arch/sys_proto.h> |
13 | #include <asm/arch-imx/cpu.h> | |
14 | #include <asm/armv8/cpu.h> | |
f3a07717 | 15 | #include <imx_thermal.h> |
cd93d625 | 16 | #include <linux/bitops.h> |
38e31978 | 17 | #include <linux/clk-provider.h> |
43c50873 PF |
18 | |
19 | DECLARE_GLOBAL_DATA_PTR; | |
20 | ||
8a8d24bd | 21 | struct cpu_imx_plat { |
43c50873 PF |
22 | const char *name; |
23 | const char *rev; | |
24 | const char *type; | |
74e8fb03 | 25 | u32 cpu_rsrc; |
43c50873 PF |
26 | u32 cpurev; |
27 | u32 freq_mhz; | |
177f9996 | 28 | u32 mpidr; |
43c50873 PF |
29 | }; |
30 | ||
38e31978 | 31 | static const char *get_imx_type_str(u32 imxtype) |
43c50873 PF |
32 | { |
33 | switch (imxtype) { | |
34 | case MXC_CPU_IMX8QXP: | |
35 | case MXC_CPU_IMX8QXP_A0: | |
38e31978 | 36 | return "8QXP"; |
43c50873 | 37 | case MXC_CPU_IMX8QM: |
38e31978 PF |
38 | return "8QM"; |
39 | case MXC_CPU_IMX93: | |
40 | return "93(52)";/* iMX93 Dual core with NPU */ | |
58da865e PF |
41 | case MXC_CPU_IMX9351: |
42 | return "93(51)";/* iMX93 Single core with NPU */ | |
43 | case MXC_CPU_IMX9332: | |
44 | return "93(32)";/* iMX93 Dual core without NPU */ | |
45 | case MXC_CPU_IMX9331: | |
46 | return "93(31)";/* iMX93 Single core without NPU */ | |
47 | case MXC_CPU_IMX9322: | |
48 | return "93(22)";/* iMX93 9x9 Dual core */ | |
49 | case MXC_CPU_IMX9321: | |
50 | return "93(21)";/* iMX93 9x9 Single core */ | |
51 | case MXC_CPU_IMX9312: | |
52 | return "93(12)";/* iMX93 9x9 Dual core without NPU */ | |
53 | case MXC_CPU_IMX9311: | |
54 | return "93(11)";/* iMX93 9x9 Single core without NPU */ | |
43c50873 PF |
55 | default: |
56 | return "??"; | |
57 | } | |
58 | } | |
59 | ||
38e31978 | 60 | static const char *get_imx_rev_str(u32 rev) |
43c50873 | 61 | { |
38e31978 PF |
62 | static char revision[4]; |
63 | ||
64 | if (IS_ENABLED(CONFIG_IMX8)) { | |
65 | switch (rev) { | |
66 | case CHIP_REV_A: | |
67 | return "A"; | |
68 | case CHIP_REV_B: | |
69 | return "B"; | |
70 | case CHIP_REV_C: | |
71 | return "C"; | |
72 | default: | |
73 | return "?"; | |
74 | } | |
75 | } else { | |
76 | revision[0] = '1' + (((rev & 0xf0) - CHIP_REV_1_0) >> 4); | |
77 | revision[1] = '.'; | |
78 | revision[2] = '0' + (rev & 0xf); | |
79 | revision[3] = '\0'; | |
80 | ||
81 | return revision; | |
43c50873 PF |
82 | } |
83 | } | |
84 | ||
74e8fb03 | 85 | static void set_core_data(struct udevice *dev) |
43c50873 | 86 | { |
8a8d24bd | 87 | struct cpu_imx_plat *plat = dev_get_plat(dev); |
74e8fb03 AG |
88 | |
89 | if (device_is_compatible(dev, "arm,cortex-a35")) { | |
90 | plat->cpu_rsrc = SC_R_A35; | |
91 | plat->name = "A35"; | |
92 | } else if (device_is_compatible(dev, "arm,cortex-a53")) { | |
93 | plat->cpu_rsrc = SC_R_A53; | |
94 | plat->name = "A53"; | |
95 | } else if (device_is_compatible(dev, "arm,cortex-a72")) { | |
96 | plat->cpu_rsrc = SC_R_A72; | |
97 | plat->name = "A72"; | |
38e31978 PF |
98 | } else if (device_is_compatible(dev, "arm,cortex-a55")) { |
99 | plat->name = "A55"; | |
74e8fb03 AG |
100 | } else { |
101 | plat->cpu_rsrc = SC_R_A53; | |
102 | plat->name = "?"; | |
103 | } | |
43c50873 PF |
104 | } |
105 | ||
7c5256e8 | 106 | #if IS_ENABLED(CONFIG_DM_THERMAL) |
8a8d24bd | 107 | static int cpu_imx_get_temp(struct cpu_imx_plat *plat) |
43c50873 PF |
108 | { |
109 | struct udevice *thermal_dev; | |
110 | int cpu_tmp, ret; | |
74e8fb03 | 111 | int idx = 1; /* use "cpu-thermal0" device */ |
43c50873 | 112 | |
7c5256e8 PF |
113 | if (IS_ENABLED(CONFIG_IMX8)) { |
114 | if (plat->cpu_rsrc == SC_R_A72) | |
115 | idx = 2; /* use "cpu-thermal1" device */ | |
116 | } else { | |
117 | idx = 1; | |
118 | } | |
43c50873 | 119 | |
74e8fb03 | 120 | ret = uclass_get_device(UCLASS_THERMAL, idx, &thermal_dev); |
43c50873 PF |
121 | if (!ret) { |
122 | ret = thermal_get_temp(thermal_dev, &cpu_tmp); | |
123 | if (ret) | |
124 | return 0xdeadbeef; | |
125 | } else { | |
126 | return 0xdeadbeef; | |
127 | } | |
128 | ||
129 | return cpu_tmp; | |
130 | } | |
131 | #else | |
8a8d24bd | 132 | static int cpu_imx_get_temp(struct cpu_imx_plat *plat) |
43c50873 PF |
133 | { |
134 | return 0; | |
135 | } | |
136 | #endif | |
137 | ||
f3a07717 PF |
138 | __weak u32 get_cpu_temp_grade(int *minc, int *maxc) |
139 | { | |
140 | return 0; | |
141 | } | |
142 | ||
3621efa7 | 143 | static int cpu_imx_get_desc(const struct udevice *dev, char *buf, int size) |
43c50873 | 144 | { |
8a8d24bd | 145 | struct cpu_imx_plat *plat = dev_get_plat(dev); |
f3a07717 | 146 | const char *grade; |
3ee6ea44 | 147 | int ret, temp; |
f3a07717 | 148 | int minc, maxc; |
43c50873 PF |
149 | |
150 | if (size < 100) | |
151 | return -ENOSPC; | |
152 | ||
38e31978 | 153 | ret = snprintf(buf, size, "NXP i.MX%s Rev%s %s at %u MHz", |
43c50873 PF |
154 | plat->type, plat->rev, plat->name, plat->freq_mhz); |
155 | ||
f3a07717 PF |
156 | if (IS_ENABLED(CONFIG_IMX9)) { |
157 | switch (get_cpu_temp_grade(&minc, &maxc)) { | |
158 | case TEMP_AUTOMOTIVE: | |
159 | grade = "Automotive temperature grade "; | |
160 | break; | |
161 | case TEMP_INDUSTRIAL: | |
162 | grade = "Industrial temperature grade "; | |
163 | break; | |
164 | case TEMP_EXTCOMMERCIAL: | |
165 | grade = "Extended Consumer temperature grade "; | |
166 | break; | |
167 | default: | |
168 | grade = "Consumer temperature grade "; | |
169 | break; | |
170 | } | |
171 | ||
172 | buf = buf + ret; | |
173 | size = size - ret; | |
174 | ret = snprintf(buf, size, "\nCPU: %s (%dC to %dC)", grade, minc, maxc); | |
175 | } | |
176 | ||
7c5256e8 | 177 | if (IS_ENABLED(CONFIG_DM_THERMAL)) { |
3ee6ea44 | 178 | temp = cpu_imx_get_temp(plat); |
43c50873 PF |
179 | buf = buf + ret; |
180 | size = size - ret; | |
3ee6ea44 YL |
181 | if (temp != 0xdeadbeef) |
182 | ret = snprintf(buf, size, " at %dC", temp); | |
183 | else | |
184 | ret = snprintf(buf, size, " - invalid sensor data"); | |
43c50873 PF |
185 | } |
186 | ||
187 | snprintf(buf + ret, size - ret, "\n"); | |
188 | ||
189 | return 0; | |
190 | } | |
191 | ||
961420fa | 192 | static int cpu_imx_get_info(const struct udevice *dev, struct cpu_info *info) |
43c50873 | 193 | { |
8a8d24bd | 194 | struct cpu_imx_plat *plat = dev_get_plat(dev); |
43c50873 PF |
195 | |
196 | info->cpu_freq = plat->freq_mhz * 1000; | |
197 | info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU); | |
198 | return 0; | |
199 | } | |
200 | ||
961420fa | 201 | static int cpu_imx_get_count(const struct udevice *dev) |
43c50873 | 202 | { |
adb3bd76 PF |
203 | ofnode node; |
204 | int num = 0; | |
205 | ||
206 | ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) { | |
207 | const char *device_type; | |
208 | ||
89090661 | 209 | if (!ofnode_is_enabled(node)) |
adb3bd76 PF |
210 | continue; |
211 | ||
212 | device_type = ofnode_read_string(node, "device_type"); | |
213 | if (!device_type) | |
214 | continue; | |
215 | ||
216 | if (!strcmp(device_type, "cpu")) | |
217 | num++; | |
218 | } | |
219 | ||
220 | return num; | |
43c50873 PF |
221 | } |
222 | ||
961420fa | 223 | static int cpu_imx_get_vendor(const struct udevice *dev, char *buf, int size) |
43c50873 PF |
224 | { |
225 | snprintf(buf, size, "NXP"); | |
226 | return 0; | |
227 | } | |
228 | ||
177f9996 PF |
229 | static int cpu_imx_is_current(struct udevice *dev) |
230 | { | |
8a8d24bd | 231 | struct cpu_imx_plat *plat = dev_get_plat(dev); |
177f9996 PF |
232 | |
233 | if (plat->mpidr == (read_mpidr() & 0xffff)) | |
234 | return 1; | |
235 | ||
236 | return 0; | |
237 | } | |
238 | ||
38e31978 | 239 | static const struct cpu_ops cpu_imx_ops = { |
43c50873 PF |
240 | .get_desc = cpu_imx_get_desc, |
241 | .get_info = cpu_imx_get_info, | |
242 | .get_count = cpu_imx_get_count, | |
243 | .get_vendor = cpu_imx_get_vendor, | |
177f9996 | 244 | .is_current = cpu_imx_is_current, |
43c50873 PF |
245 | }; |
246 | ||
38e31978 | 247 | static const struct udevice_id cpu_imx_ids[] = { |
43c50873 PF |
248 | { .compatible = "arm,cortex-a35" }, |
249 | { .compatible = "arm,cortex-a53" }, | |
38e31978 | 250 | { .compatible = "arm,cortex-a55" }, |
177f9996 | 251 | { .compatible = "arm,cortex-a72" }, |
43c50873 PF |
252 | { } |
253 | }; | |
254 | ||
38e31978 | 255 | static ulong imx_get_cpu_rate(struct udevice *dev) |
43c50873 | 256 | { |
8a8d24bd | 257 | struct cpu_imx_plat *plat = dev_get_plat(dev); |
38e31978 | 258 | struct clk clk; |
43c50873 | 259 | ulong rate; |
74e8fb03 | 260 | int ret; |
43c50873 | 261 | |
38e31978 PF |
262 | if (IS_ENABLED(CONFIG_IMX8)) { |
263 | ret = sc_pm_get_clock_rate(-1, plat->cpu_rsrc, SC_PM_CLK_CPU, | |
264 | (sc_pm_clock_rate_t *)&rate); | |
265 | } else { | |
266 | ret = clk_get_by_index(dev, 0, &clk); | |
267 | if (!ret) { | |
268 | rate = clk_get_rate(&clk); | |
269 | if (!rate) | |
270 | ret = -EOPNOTSUPP; | |
271 | } | |
272 | } | |
43c50873 PF |
273 | if (ret) { |
274 | printf("Could not read CPU frequency: %d\n", ret); | |
275 | return 0; | |
276 | } | |
277 | ||
278 | return rate; | |
279 | } | |
280 | ||
38e31978 | 281 | static int imx_cpu_probe(struct udevice *dev) |
43c50873 | 282 | { |
8a8d24bd | 283 | struct cpu_imx_plat *plat = dev_get_plat(dev); |
43c50873 PF |
284 | u32 cpurev; |
285 | ||
74e8fb03 | 286 | set_core_data(dev); |
43c50873 PF |
287 | cpurev = get_cpu_rev(); |
288 | plat->cpurev = cpurev; | |
38e31978 PF |
289 | plat->rev = get_imx_rev_str(cpurev & 0xFFF); |
290 | plat->type = get_imx_type_str((cpurev & 0xFF000) >> 12); | |
291 | plat->freq_mhz = imx_get_cpu_rate(dev) / 1000000; | |
177f9996 PF |
292 | plat->mpidr = dev_read_addr(dev); |
293 | if (plat->mpidr == FDT_ADDR_T_NONE) { | |
294 | printf("%s: Failed to get CPU reg property\n", __func__); | |
295 | return -EINVAL; | |
296 | } | |
297 | ||
43c50873 PF |
298 | return 0; |
299 | } | |
300 | ||
38e31978 PF |
301 | U_BOOT_DRIVER(cpu_imx_drv) = { |
302 | .name = "imx_cpu", | |
43c50873 | 303 | .id = UCLASS_CPU, |
38e31978 PF |
304 | .of_match = cpu_imx_ids, |
305 | .ops = &cpu_imx_ops, | |
306 | .probe = imx_cpu_probe, | |
8a8d24bd | 307 | .plat_auto = sizeof(struct cpu_imx_plat), |
43c50873 PF |
308 | .flags = DM_FLAG_PRE_RELOC, |
309 | }; |