Commit | Line | Data |
---|---|---|
43c50873 PF |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright 2019 NXP | |
4 | */ | |
5 | ||
6 | #include <common.h> | |
7 | #include <cpu.h> | |
8 | #include <dm.h> | |
9 | #include <thermal.h> | |
401d1c4f | 10 | #include <asm/global_data.h> |
90526e9f | 11 | #include <asm/system.h> |
43c50873 PF |
12 | #include <asm/arch/sci/sci.h> |
13 | #include <asm/arch/sys_proto.h> | |
14 | #include <asm/arch-imx/cpu.h> | |
15 | #include <asm/armv8/cpu.h> | |
cd93d625 | 16 | #include <linux/bitops.h> |
43c50873 PF |
17 | |
18 | DECLARE_GLOBAL_DATA_PTR; | |
19 | ||
8a8d24bd | 20 | struct cpu_imx_plat { |
43c50873 PF |
21 | const char *name; |
22 | const char *rev; | |
23 | const char *type; | |
74e8fb03 | 24 | u32 cpu_rsrc; |
43c50873 PF |
25 | u32 cpurev; |
26 | u32 freq_mhz; | |
177f9996 | 27 | u32 mpidr; |
43c50873 PF |
28 | }; |
29 | ||
30 | const char *get_imx8_type(u32 imxtype) | |
31 | { | |
32 | switch (imxtype) { | |
33 | case MXC_CPU_IMX8QXP: | |
34 | case MXC_CPU_IMX8QXP_A0: | |
35 | return "QXP"; | |
36 | case MXC_CPU_IMX8QM: | |
37 | return "QM"; | |
38 | default: | |
39 | return "??"; | |
40 | } | |
41 | } | |
42 | ||
43 | const char *get_imx8_rev(u32 rev) | |
44 | { | |
45 | switch (rev) { | |
46 | case CHIP_REV_A: | |
47 | return "A"; | |
48 | case CHIP_REV_B: | |
49 | return "B"; | |
8142a97d FL |
50 | case CHIP_REV_C: |
51 | return "C"; | |
43c50873 PF |
52 | default: |
53 | return "?"; | |
54 | } | |
55 | } | |
56 | ||
74e8fb03 | 57 | static void set_core_data(struct udevice *dev) |
43c50873 | 58 | { |
8a8d24bd | 59 | struct cpu_imx_plat *plat = dev_get_plat(dev); |
74e8fb03 AG |
60 | |
61 | if (device_is_compatible(dev, "arm,cortex-a35")) { | |
62 | plat->cpu_rsrc = SC_R_A35; | |
63 | plat->name = "A35"; | |
64 | } else if (device_is_compatible(dev, "arm,cortex-a53")) { | |
65 | plat->cpu_rsrc = SC_R_A53; | |
66 | plat->name = "A53"; | |
67 | } else if (device_is_compatible(dev, "arm,cortex-a72")) { | |
68 | plat->cpu_rsrc = SC_R_A72; | |
69 | plat->name = "A72"; | |
70 | } else { | |
71 | plat->cpu_rsrc = SC_R_A53; | |
72 | plat->name = "?"; | |
73 | } | |
43c50873 PF |
74 | } |
75 | ||
76 | #if IS_ENABLED(CONFIG_IMX_SCU_THERMAL) | |
8a8d24bd | 77 | static int cpu_imx_get_temp(struct cpu_imx_plat *plat) |
43c50873 PF |
78 | { |
79 | struct udevice *thermal_dev; | |
80 | int cpu_tmp, ret; | |
74e8fb03 | 81 | int idx = 1; /* use "cpu-thermal0" device */ |
43c50873 | 82 | |
74e8fb03 AG |
83 | if (plat->cpu_rsrc == SC_R_A72) |
84 | idx = 2; /* use "cpu-thermal1" device */ | |
43c50873 | 85 | |
74e8fb03 | 86 | ret = uclass_get_device(UCLASS_THERMAL, idx, &thermal_dev); |
43c50873 PF |
87 | if (!ret) { |
88 | ret = thermal_get_temp(thermal_dev, &cpu_tmp); | |
89 | if (ret) | |
90 | return 0xdeadbeef; | |
91 | } else { | |
92 | return 0xdeadbeef; | |
93 | } | |
94 | ||
95 | return cpu_tmp; | |
96 | } | |
97 | #else | |
8a8d24bd | 98 | static int cpu_imx_get_temp(struct cpu_imx_plat *plat) |
43c50873 PF |
99 | { |
100 | return 0; | |
101 | } | |
102 | #endif | |
103 | ||
961420fa | 104 | int cpu_imx_get_desc(const struct udevice *dev, char *buf, int size) |
43c50873 | 105 | { |
8a8d24bd | 106 | struct cpu_imx_plat *plat = dev_get_plat(dev); |
3ee6ea44 | 107 | int ret, temp; |
43c50873 PF |
108 | |
109 | if (size < 100) | |
110 | return -ENOSPC; | |
111 | ||
112 | ret = snprintf(buf, size, "NXP i.MX8%s Rev%s %s at %u MHz", | |
113 | plat->type, plat->rev, plat->name, plat->freq_mhz); | |
114 | ||
115 | if (IS_ENABLED(CONFIG_IMX_SCU_THERMAL)) { | |
3ee6ea44 | 116 | temp = cpu_imx_get_temp(plat); |
43c50873 PF |
117 | buf = buf + ret; |
118 | size = size - ret; | |
3ee6ea44 YL |
119 | if (temp != 0xdeadbeef) |
120 | ret = snprintf(buf, size, " at %dC", temp); | |
121 | else | |
122 | ret = snprintf(buf, size, " - invalid sensor data"); | |
43c50873 PF |
123 | } |
124 | ||
125 | snprintf(buf + ret, size - ret, "\n"); | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
961420fa | 130 | static int cpu_imx_get_info(const struct udevice *dev, struct cpu_info *info) |
43c50873 | 131 | { |
8a8d24bd | 132 | struct cpu_imx_plat *plat = dev_get_plat(dev); |
43c50873 PF |
133 | |
134 | info->cpu_freq = plat->freq_mhz * 1000; | |
135 | info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU); | |
136 | return 0; | |
137 | } | |
138 | ||
961420fa | 139 | static int cpu_imx_get_count(const struct udevice *dev) |
43c50873 | 140 | { |
adb3bd76 PF |
141 | ofnode node; |
142 | int num = 0; | |
143 | ||
144 | ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) { | |
145 | const char *device_type; | |
146 | ||
147 | if (!ofnode_is_available(node)) | |
148 | continue; | |
149 | ||
150 | device_type = ofnode_read_string(node, "device_type"); | |
151 | if (!device_type) | |
152 | continue; | |
153 | ||
154 | if (!strcmp(device_type, "cpu")) | |
155 | num++; | |
156 | } | |
157 | ||
158 | return num; | |
43c50873 PF |
159 | } |
160 | ||
961420fa | 161 | static int cpu_imx_get_vendor(const struct udevice *dev, char *buf, int size) |
43c50873 PF |
162 | { |
163 | snprintf(buf, size, "NXP"); | |
164 | return 0; | |
165 | } | |
166 | ||
177f9996 PF |
167 | static int cpu_imx_is_current(struct udevice *dev) |
168 | { | |
8a8d24bd | 169 | struct cpu_imx_plat *plat = dev_get_plat(dev); |
177f9996 PF |
170 | |
171 | if (plat->mpidr == (read_mpidr() & 0xffff)) | |
172 | return 1; | |
173 | ||
174 | return 0; | |
175 | } | |
176 | ||
43c50873 PF |
177 | static const struct cpu_ops cpu_imx8_ops = { |
178 | .get_desc = cpu_imx_get_desc, | |
179 | .get_info = cpu_imx_get_info, | |
180 | .get_count = cpu_imx_get_count, | |
181 | .get_vendor = cpu_imx_get_vendor, | |
177f9996 | 182 | .is_current = cpu_imx_is_current, |
43c50873 PF |
183 | }; |
184 | ||
185 | static const struct udevice_id cpu_imx8_ids[] = { | |
186 | { .compatible = "arm,cortex-a35" }, | |
187 | { .compatible = "arm,cortex-a53" }, | |
177f9996 | 188 | { .compatible = "arm,cortex-a72" }, |
43c50873 PF |
189 | { } |
190 | }; | |
191 | ||
55bc96f3 | 192 | static ulong imx8_get_cpu_rate(struct udevice *dev) |
43c50873 | 193 | { |
8a8d24bd | 194 | struct cpu_imx_plat *plat = dev_get_plat(dev); |
43c50873 | 195 | ulong rate; |
74e8fb03 | 196 | int ret; |
43c50873 | 197 | |
74e8fb03 | 198 | ret = sc_pm_get_clock_rate(-1, plat->cpu_rsrc, SC_PM_CLK_CPU, |
43c50873 PF |
199 | (sc_pm_clock_rate_t *)&rate); |
200 | if (ret) { | |
201 | printf("Could not read CPU frequency: %d\n", ret); | |
202 | return 0; | |
203 | } | |
204 | ||
205 | return rate; | |
206 | } | |
207 | ||
208 | static int imx8_cpu_probe(struct udevice *dev) | |
209 | { | |
8a8d24bd | 210 | struct cpu_imx_plat *plat = dev_get_plat(dev); |
43c50873 PF |
211 | u32 cpurev; |
212 | ||
74e8fb03 | 213 | set_core_data(dev); |
43c50873 PF |
214 | cpurev = get_cpu_rev(); |
215 | plat->cpurev = cpurev; | |
43c50873 PF |
216 | plat->rev = get_imx8_rev(cpurev & 0xFFF); |
217 | plat->type = get_imx8_type((cpurev & 0xFF000) >> 12); | |
55bc96f3 | 218 | plat->freq_mhz = imx8_get_cpu_rate(dev) / 1000000; |
177f9996 PF |
219 | plat->mpidr = dev_read_addr(dev); |
220 | if (plat->mpidr == FDT_ADDR_T_NONE) { | |
221 | printf("%s: Failed to get CPU reg property\n", __func__); | |
222 | return -EINVAL; | |
223 | } | |
224 | ||
43c50873 PF |
225 | return 0; |
226 | } | |
227 | ||
228 | U_BOOT_DRIVER(cpu_imx8_drv) = { | |
229 | .name = "imx8x_cpu", | |
230 | .id = UCLASS_CPU, | |
231 | .of_match = cpu_imx8_ids, | |
232 | .ops = &cpu_imx8_ops, | |
233 | .probe = imx8_cpu_probe, | |
8a8d24bd | 234 | .plat_auto = sizeof(struct cpu_imx_plat), |
43c50873 PF |
235 | .flags = DM_FLAG_PRE_RELOC, |
236 | }; |