1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Renesas SoC Identification |
4 | * |
5 | * Copyright (C) 2014-2016 Glider bvba |
6 | */ |
7 | |
8 | #include <linux/io.h> |
9 | #include <linux/of.h> |
10 | #include <linux/of_address.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/string.h> |
13 | #include <linux/sys_soc.h> |
14 | |
15 | struct renesas_family { |
16 | const char name[16]; |
17 | u32 reg; /* CCCR or PRR, if not in DT */ |
18 | }; |
19 | |
20 | static const struct renesas_family fam_rcar_gen1 __initconst __maybe_unused = { |
21 | .name = "R-Car Gen1" , |
22 | .reg = 0xff000044, /* PRR (Product Register) */ |
23 | }; |
24 | |
25 | static const struct renesas_family fam_rcar_gen2 __initconst __maybe_unused = { |
26 | .name = "R-Car Gen2" , |
27 | .reg = 0xff000044, /* PRR (Product Register) */ |
28 | }; |
29 | |
30 | static const struct renesas_family fam_rcar_gen3 __initconst __maybe_unused = { |
31 | .name = "R-Car Gen3" , |
32 | .reg = 0xfff00044, /* PRR (Product Register) */ |
33 | }; |
34 | |
35 | static const struct renesas_family fam_rcar_gen4 __initconst __maybe_unused = { |
36 | .name = "R-Car Gen4" , |
37 | }; |
38 | |
39 | static const struct renesas_family fam_rmobile __initconst __maybe_unused = { |
40 | .name = "R-Mobile" , |
41 | .reg = 0xe600101c, /* CCCR (Common Chip Code Register) */ |
42 | }; |
43 | |
44 | static const struct renesas_family fam_rza1 __initconst __maybe_unused = { |
45 | .name = "RZ/A1" , |
46 | }; |
47 | |
48 | static const struct renesas_family fam_rza2 __initconst __maybe_unused = { |
49 | .name = "RZ/A2" , |
50 | }; |
51 | |
52 | static const struct renesas_family fam_rzfive __initconst __maybe_unused = { |
53 | .name = "RZ/Five" , |
54 | }; |
55 | |
56 | static const struct renesas_family fam_rzg1 __initconst __maybe_unused = { |
57 | .name = "RZ/G1" , |
58 | .reg = 0xff000044, /* PRR (Product Register) */ |
59 | }; |
60 | |
61 | static const struct renesas_family fam_rzg2 __initconst __maybe_unused = { |
62 | .name = "RZ/G2" , |
63 | .reg = 0xfff00044, /* PRR (Product Register) */ |
64 | }; |
65 | |
66 | static const struct renesas_family fam_rzg2l __initconst __maybe_unused = { |
67 | .name = "RZ/G2L" , |
68 | }; |
69 | |
70 | static const struct renesas_family fam_rzg2ul __initconst __maybe_unused = { |
71 | .name = "RZ/G2UL" , |
72 | }; |
73 | |
74 | static const struct renesas_family fam_rzg3s __initconst __maybe_unused = { |
75 | .name = "RZ/G3S" , |
76 | }; |
77 | |
78 | static const struct renesas_family fam_rzv2l __initconst __maybe_unused = { |
79 | .name = "RZ/V2L" , |
80 | }; |
81 | |
82 | static const struct renesas_family fam_rzv2m __initconst __maybe_unused = { |
83 | .name = "RZ/V2M" , |
84 | }; |
85 | |
86 | static const struct renesas_family fam_shmobile __initconst __maybe_unused = { |
87 | .name = "SH-Mobile" , |
88 | .reg = 0xe600101c, /* CCCR (Common Chip Code Register) */ |
89 | }; |
90 | |
91 | struct renesas_soc { |
92 | const struct renesas_family *family; |
93 | u32 id; |
94 | }; |
95 | |
96 | static const struct renesas_soc soc_rz_a1h __initconst __maybe_unused = { |
97 | .family = &fam_rza1, |
98 | }; |
99 | |
100 | static const struct renesas_soc soc_rz_a2m __initconst __maybe_unused = { |
101 | .family = &fam_rza2, |
102 | .id = 0x3b, |
103 | }; |
104 | |
105 | static const struct renesas_soc soc_rmobile_ape6 __initconst __maybe_unused = { |
106 | .family = &fam_rmobile, |
107 | .id = 0x3f, |
108 | }; |
109 | |
110 | static const struct renesas_soc soc_rmobile_a1 __initconst __maybe_unused = { |
111 | .family = &fam_rmobile, |
112 | .id = 0x40, |
113 | }; |
114 | |
115 | static const struct renesas_soc soc_rz_five __initconst __maybe_unused = { |
116 | .family = &fam_rzfive, |
117 | .id = 0x847c447, |
118 | }; |
119 | |
120 | static const struct renesas_soc soc_rz_g1h __initconst __maybe_unused = { |
121 | .family = &fam_rzg1, |
122 | .id = 0x45, |
123 | }; |
124 | |
125 | static const struct renesas_soc soc_rz_g1m __initconst __maybe_unused = { |
126 | .family = &fam_rzg1, |
127 | .id = 0x47, |
128 | }; |
129 | |
130 | static const struct renesas_soc soc_rz_g1n __initconst __maybe_unused = { |
131 | .family = &fam_rzg1, |
132 | .id = 0x4b, |
133 | }; |
134 | |
135 | static const struct renesas_soc soc_rz_g1e __initconst __maybe_unused = { |
136 | .family = &fam_rzg1, |
137 | .id = 0x4c, |
138 | }; |
139 | |
140 | static const struct renesas_soc soc_rz_g1c __initconst __maybe_unused = { |
141 | .family = &fam_rzg1, |
142 | .id = 0x53, |
143 | }; |
144 | |
145 | static const struct renesas_soc soc_rz_g2m __initconst __maybe_unused = { |
146 | .family = &fam_rzg2, |
147 | .id = 0x52, |
148 | }; |
149 | |
150 | static const struct renesas_soc soc_rz_g2n __initconst __maybe_unused = { |
151 | .family = &fam_rzg2, |
152 | .id = 0x55, |
153 | }; |
154 | |
155 | static const struct renesas_soc soc_rz_g2e __initconst __maybe_unused = { |
156 | .family = &fam_rzg2, |
157 | .id = 0x57, |
158 | }; |
159 | |
160 | static const struct renesas_soc soc_rz_g2h __initconst __maybe_unused = { |
161 | .family = &fam_rzg2, |
162 | .id = 0x4f, |
163 | }; |
164 | |
165 | static const struct renesas_soc soc_rz_g2l __initconst __maybe_unused = { |
166 | .family = &fam_rzg2l, |
167 | .id = 0x841c447, |
168 | }; |
169 | |
170 | static const struct renesas_soc soc_rz_g2ul __initconst __maybe_unused = { |
171 | .family = &fam_rzg2ul, |
172 | .id = 0x8450447, |
173 | }; |
174 | |
175 | static const struct renesas_soc soc_rz_g3s __initconst __maybe_unused = { |
176 | .family = &fam_rzg3s, |
177 | .id = 0x85e0447, |
178 | }; |
179 | |
180 | static const struct renesas_soc soc_rz_v2l __initconst __maybe_unused = { |
181 | .family = &fam_rzv2l, |
182 | .id = 0x8447447, |
183 | }; |
184 | |
185 | static const struct renesas_soc soc_rz_v2m __initconst __maybe_unused = { |
186 | .family = &fam_rzv2m, |
187 | }; |
188 | |
189 | static const struct renesas_soc soc_rcar_m1a __initconst __maybe_unused = { |
190 | .family = &fam_rcar_gen1, |
191 | }; |
192 | |
193 | static const struct renesas_soc soc_rcar_h1 __initconst __maybe_unused = { |
194 | .family = &fam_rcar_gen1, |
195 | .id = 0x3b, |
196 | }; |
197 | |
198 | static const struct renesas_soc soc_rcar_h2 __initconst __maybe_unused = { |
199 | .family = &fam_rcar_gen2, |
200 | .id = 0x45, |
201 | }; |
202 | |
203 | static const struct renesas_soc soc_rcar_m2_w __initconst __maybe_unused = { |
204 | .family = &fam_rcar_gen2, |
205 | .id = 0x47, |
206 | }; |
207 | |
208 | static const struct renesas_soc soc_rcar_v2h __initconst __maybe_unused = { |
209 | .family = &fam_rcar_gen2, |
210 | .id = 0x4a, |
211 | }; |
212 | |
213 | static const struct renesas_soc soc_rcar_m2_n __initconst __maybe_unused = { |
214 | .family = &fam_rcar_gen2, |
215 | .id = 0x4b, |
216 | }; |
217 | |
218 | static const struct renesas_soc soc_rcar_e2 __initconst __maybe_unused = { |
219 | .family = &fam_rcar_gen2, |
220 | .id = 0x4c, |
221 | }; |
222 | |
223 | static const struct renesas_soc soc_rcar_h3 __initconst __maybe_unused = { |
224 | .family = &fam_rcar_gen3, |
225 | .id = 0x4f, |
226 | }; |
227 | |
228 | static const struct renesas_soc soc_rcar_m3_w __initconst __maybe_unused = { |
229 | .family = &fam_rcar_gen3, |
230 | .id = 0x52, |
231 | }; |
232 | |
233 | static const struct renesas_soc soc_rcar_m3_n __initconst __maybe_unused = { |
234 | .family = &fam_rcar_gen3, |
235 | .id = 0x55, |
236 | }; |
237 | |
238 | static const struct renesas_soc soc_rcar_v3m __initconst __maybe_unused = { |
239 | .family = &fam_rcar_gen3, |
240 | .id = 0x54, |
241 | }; |
242 | |
243 | static const struct renesas_soc soc_rcar_v3h __initconst __maybe_unused = { |
244 | .family = &fam_rcar_gen3, |
245 | .id = 0x56, |
246 | }; |
247 | |
248 | static const struct renesas_soc soc_rcar_e3 __initconst __maybe_unused = { |
249 | .family = &fam_rcar_gen3, |
250 | .id = 0x57, |
251 | }; |
252 | |
253 | static const struct renesas_soc soc_rcar_d3 __initconst __maybe_unused = { |
254 | .family = &fam_rcar_gen3, |
255 | .id = 0x58, |
256 | }; |
257 | |
258 | static const struct renesas_soc soc_rcar_v3u __initconst __maybe_unused = { |
259 | .family = &fam_rcar_gen4, |
260 | .id = 0x59, |
261 | }; |
262 | |
263 | static const struct renesas_soc soc_rcar_s4 __initconst __maybe_unused = { |
264 | .family = &fam_rcar_gen4, |
265 | .id = 0x5a, |
266 | }; |
267 | |
268 | static const struct renesas_soc soc_rcar_v4h __initconst __maybe_unused = { |
269 | .family = &fam_rcar_gen4, |
270 | .id = 0x5c, |
271 | }; |
272 | |
273 | static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = { |
274 | .family = &fam_shmobile, |
275 | .id = 0x37, |
276 | }; |
277 | |
278 | |
279 | static const struct of_device_id renesas_socs[] __initconst __maybe_unused = { |
280 | #ifdef CONFIG_ARCH_R7S72100 |
281 | { .compatible = "renesas,r7s72100" , .data = &soc_rz_a1h }, |
282 | #endif |
283 | #ifdef CONFIG_ARCH_R7S9210 |
284 | { .compatible = "renesas,r7s9210" , .data = &soc_rz_a2m }, |
285 | #endif |
286 | #ifdef CONFIG_ARCH_R8A73A4 |
287 | { .compatible = "renesas,r8a73a4" , .data = &soc_rmobile_ape6 }, |
288 | #endif |
289 | #ifdef CONFIG_ARCH_R8A7740 |
290 | { .compatible = "renesas,r8a7740" , .data = &soc_rmobile_a1 }, |
291 | #endif |
292 | #ifdef CONFIG_ARCH_R8A7742 |
293 | { .compatible = "renesas,r8a7742" , .data = &soc_rz_g1h }, |
294 | #endif |
295 | #ifdef CONFIG_ARCH_R8A7743 |
296 | { .compatible = "renesas,r8a7743" , .data = &soc_rz_g1m }, |
297 | #endif |
298 | #ifdef CONFIG_ARCH_R8A7744 |
299 | { .compatible = "renesas,r8a7744" , .data = &soc_rz_g1n }, |
300 | #endif |
301 | #ifdef CONFIG_ARCH_R8A7745 |
302 | { .compatible = "renesas,r8a7745" , .data = &soc_rz_g1e }, |
303 | #endif |
304 | #ifdef CONFIG_ARCH_R8A77470 |
305 | { .compatible = "renesas,r8a77470" , .data = &soc_rz_g1c }, |
306 | #endif |
307 | #ifdef CONFIG_ARCH_R8A774A1 |
308 | { .compatible = "renesas,r8a774a1" , .data = &soc_rz_g2m }, |
309 | #endif |
310 | #ifdef CONFIG_ARCH_R8A774B1 |
311 | { .compatible = "renesas,r8a774b1" , .data = &soc_rz_g2n }, |
312 | #endif |
313 | #ifdef CONFIG_ARCH_R8A774C0 |
314 | { .compatible = "renesas,r8a774c0" , .data = &soc_rz_g2e }, |
315 | #endif |
316 | #ifdef CONFIG_ARCH_R8A774E1 |
317 | { .compatible = "renesas,r8a774e1" , .data = &soc_rz_g2h }, |
318 | #endif |
319 | #ifdef CONFIG_ARCH_R8A7778 |
320 | { .compatible = "renesas,r8a7778" , .data = &soc_rcar_m1a }, |
321 | #endif |
322 | #ifdef CONFIG_ARCH_R8A7779 |
323 | { .compatible = "renesas,r8a7779" , .data = &soc_rcar_h1 }, |
324 | #endif |
325 | #ifdef CONFIG_ARCH_R8A7790 |
326 | { .compatible = "renesas,r8a7790" , .data = &soc_rcar_h2 }, |
327 | #endif |
328 | #ifdef CONFIG_ARCH_R8A7791 |
329 | { .compatible = "renesas,r8a7791" , .data = &soc_rcar_m2_w }, |
330 | #endif |
331 | #ifdef CONFIG_ARCH_R8A7792 |
332 | { .compatible = "renesas,r8a7792" , .data = &soc_rcar_v2h }, |
333 | #endif |
334 | #ifdef CONFIG_ARCH_R8A7793 |
335 | { .compatible = "renesas,r8a7793" , .data = &soc_rcar_m2_n }, |
336 | #endif |
337 | #ifdef CONFIG_ARCH_R8A7794 |
338 | { .compatible = "renesas,r8a7794" , .data = &soc_rcar_e2 }, |
339 | #endif |
340 | #ifdef CONFIG_ARCH_R8A77951 |
341 | { .compatible = "renesas,r8a7795" , .data = &soc_rcar_h3 }, |
342 | { .compatible = "renesas,r8a779m0" , .data = &soc_rcar_h3 }, |
343 | { .compatible = "renesas,r8a779m1" , .data = &soc_rcar_h3 }, |
344 | { .compatible = "renesas,r8a779m8" , .data = &soc_rcar_h3 }, |
345 | { .compatible = "renesas,r8a779mb" , .data = &soc_rcar_h3 }, |
346 | #endif |
347 | #ifdef CONFIG_ARCH_R8A77960 |
348 | { .compatible = "renesas,r8a7796" , .data = &soc_rcar_m3_w }, |
349 | #endif |
350 | #ifdef CONFIG_ARCH_R8A77961 |
351 | { .compatible = "renesas,r8a77961" , .data = &soc_rcar_m3_w }, |
352 | { .compatible = "renesas,r8a779m2" , .data = &soc_rcar_m3_w }, |
353 | { .compatible = "renesas,r8a779m3" , .data = &soc_rcar_m3_w }, |
354 | #endif |
355 | #ifdef CONFIG_ARCH_R8A77965 |
356 | { .compatible = "renesas,r8a77965" , .data = &soc_rcar_m3_n }, |
357 | { .compatible = "renesas,r8a779m4" , .data = &soc_rcar_m3_n }, |
358 | { .compatible = "renesas,r8a779m5" , .data = &soc_rcar_m3_n }, |
359 | #endif |
360 | #ifdef CONFIG_ARCH_R8A77970 |
361 | { .compatible = "renesas,r8a77970" , .data = &soc_rcar_v3m }, |
362 | #endif |
363 | #ifdef CONFIG_ARCH_R8A77980 |
364 | { .compatible = "renesas,r8a77980" , .data = &soc_rcar_v3h }, |
365 | #endif |
366 | #ifdef CONFIG_ARCH_R8A77990 |
367 | { .compatible = "renesas,r8a77990" , .data = &soc_rcar_e3 }, |
368 | { .compatible = "renesas,r8a779m6" , .data = &soc_rcar_e3 }, |
369 | #endif |
370 | #ifdef CONFIG_ARCH_R8A77995 |
371 | { .compatible = "renesas,r8a77995" , .data = &soc_rcar_d3 }, |
372 | { .compatible = "renesas,r8a779m7" , .data = &soc_rcar_d3 }, |
373 | #endif |
374 | #ifdef CONFIG_ARCH_R8A779A0 |
375 | { .compatible = "renesas,r8a779a0" , .data = &soc_rcar_v3u }, |
376 | #endif |
377 | #ifdef CONFIG_ARCH_R8A779F0 |
378 | { .compatible = "renesas,r8a779f0" , .data = &soc_rcar_s4 }, |
379 | #endif |
380 | #ifdef CONFIG_ARCH_R8A779G0 |
381 | { .compatible = "renesas,r8a779g0" , .data = &soc_rcar_v4h }, |
382 | #endif |
383 | #ifdef CONFIG_ARCH_R9A07G043 |
384 | #ifdef CONFIG_RISCV |
385 | { .compatible = "renesas,r9a07g043" , .data = &soc_rz_five }, |
386 | #else |
387 | { .compatible = "renesas,r9a07g043" , .data = &soc_rz_g2ul }, |
388 | #endif |
389 | #endif |
390 | #ifdef CONFIG_ARCH_R9A07G044 |
391 | { .compatible = "renesas,r9a07g044" , .data = &soc_rz_g2l }, |
392 | #endif |
393 | #ifdef CONFIG_ARCH_R9A07G054 |
394 | { .compatible = "renesas,r9a07g054" , .data = &soc_rz_v2l }, |
395 | #endif |
396 | #ifdef CONFIG_ARCH_R9A08G045 |
397 | { .compatible = "renesas,r9a08g045" , .data = &soc_rz_g3s }, |
398 | #endif |
399 | #ifdef CONFIG_ARCH_R9A09G011 |
400 | { .compatible = "renesas,r9a09g011" , .data = &soc_rz_v2m }, |
401 | #endif |
402 | #ifdef CONFIG_ARCH_SH73A0 |
403 | { .compatible = "renesas,sh73a0" , .data = &soc_shmobile_ag5 }, |
404 | #endif |
405 | { /* sentinel */ } |
406 | }; |
407 | |
408 | struct renesas_id { |
409 | unsigned int offset; |
410 | u32 mask; |
411 | }; |
412 | |
413 | static const struct renesas_id id_bsid __initconst = { |
414 | .offset = 0, |
415 | .mask = 0xff0000, |
416 | /* |
417 | * TODO: Upper 4 bits of BSID are for chip version, but the format is |
418 | * not known at this time so we don't know how to specify eshi and eslo |
419 | */ |
420 | }; |
421 | |
422 | static const struct renesas_id id_rzg2l __initconst = { |
423 | .offset = 0xa04, |
424 | .mask = 0xfffffff, |
425 | }; |
426 | |
427 | static const struct renesas_id id_rzv2m __initconst = { |
428 | .offset = 0x104, |
429 | .mask = 0xff, |
430 | }; |
431 | |
432 | static const struct renesas_id id_prr __initconst = { |
433 | .offset = 0, |
434 | .mask = 0xff00, |
435 | }; |
436 | |
437 | static const struct of_device_id renesas_ids[] __initconst = { |
438 | { .compatible = "renesas,bsid" , .data = &id_bsid }, |
439 | { .compatible = "renesas,r9a07g043-sysc" , .data = &id_rzg2l }, |
440 | { .compatible = "renesas,r9a07g044-sysc" , .data = &id_rzg2l }, |
441 | { .compatible = "renesas,r9a07g054-sysc" , .data = &id_rzg2l }, |
442 | { .compatible = "renesas,r9a08g045-sysc" , .data = &id_rzg2l }, |
443 | { .compatible = "renesas,r9a09g011-sys" , .data = &id_rzv2m }, |
444 | { .compatible = "renesas,prr" , .data = &id_prr }, |
445 | { /* sentinel */ } |
446 | }; |
447 | |
448 | static int __init renesas_soc_init(void) |
449 | { |
450 | struct soc_device_attribute *soc_dev_attr; |
451 | unsigned int product, eshi = 0, eslo; |
452 | const struct renesas_family *family; |
453 | const struct of_device_id *match; |
454 | const struct renesas_soc *soc; |
455 | const struct renesas_id *id; |
456 | void __iomem *chipid = NULL; |
457 | const char *rev_prefix = "" ; |
458 | struct soc_device *soc_dev; |
459 | struct device_node *np; |
460 | const char *soc_id; |
461 | int ret; |
462 | |
463 | match = of_match_node(matches: renesas_socs, node: of_root); |
464 | if (!match) |
465 | return -ENODEV; |
466 | |
467 | soc_id = strchr(match->compatible, ',') + 1; |
468 | soc = match->data; |
469 | family = soc->family; |
470 | |
471 | np = of_find_matching_node_and_match(NULL, matches: renesas_ids, match: &match); |
472 | if (np) { |
473 | id = match->data; |
474 | chipid = of_iomap(node: np, index: 0); |
475 | of_node_put(node: np); |
476 | } else if (soc->id && family->reg) { |
477 | /* Try hardcoded CCCR/PRR fallback */ |
478 | id = &id_prr; |
479 | chipid = ioremap(offset: family->reg, size: 4); |
480 | } |
481 | |
482 | soc_dev_attr = kzalloc(size: sizeof(*soc_dev_attr), GFP_KERNEL); |
483 | if (!soc_dev_attr) { |
484 | if (chipid) |
485 | iounmap(addr: chipid); |
486 | return -ENOMEM; |
487 | } |
488 | |
489 | np = of_find_node_by_path(path: "/" ); |
490 | of_property_read_string(np, propname: "model" , out_string: &soc_dev_attr->machine); |
491 | of_node_put(node: np); |
492 | |
493 | soc_dev_attr->family = kstrdup_const(s: family->name, GFP_KERNEL); |
494 | soc_dev_attr->soc_id = kstrdup_const(s: soc_id, GFP_KERNEL); |
495 | |
496 | if (chipid) { |
497 | product = readl(addr: chipid + id->offset); |
498 | iounmap(addr: chipid); |
499 | |
500 | if (id == &id_prr) { |
501 | /* R-Car M3-W ES1.1 incorrectly identifies as ES2.0 */ |
502 | if ((product & 0x7fff) == 0x5210) |
503 | product ^= 0x11; |
504 | /* R-Car M3-W ES1.3 incorrectly identifies as ES2.1 */ |
505 | if ((product & 0x7fff) == 0x5211) |
506 | product ^= 0x12; |
507 | |
508 | eshi = ((product >> 4) & 0x0f) + 1; |
509 | eslo = product & 0xf; |
510 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, fmt: "ES%u.%u" , |
511 | eshi, eslo); |
512 | } else if (id == &id_rzg2l) { |
513 | eshi = ((product >> 28) & 0x0f); |
514 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, fmt: "%u" , |
515 | eshi); |
516 | rev_prefix = "Rev " ; |
517 | } else if (id == &id_rzv2m) { |
518 | eshi = ((product >> 4) & 0x0f); |
519 | eslo = product & 0xf; |
520 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, fmt: "%u.%u" , |
521 | eshi, eslo); |
522 | } |
523 | |
524 | if (soc->id && |
525 | ((product & id->mask) >> __ffs(id->mask)) != soc->id) { |
526 | pr_warn("SoC mismatch (product = 0x%x)\n" , product); |
527 | ret = -ENODEV; |
528 | goto free_soc_dev_attr; |
529 | } |
530 | } |
531 | |
532 | pr_info("Detected Renesas %s %s %s%s\n" , soc_dev_attr->family, |
533 | soc_dev_attr->soc_id, rev_prefix, soc_dev_attr->revision ?: "" ); |
534 | |
535 | soc_dev = soc_device_register(soc_plat_dev_attr: soc_dev_attr); |
536 | if (IS_ERR(ptr: soc_dev)) { |
537 | ret = PTR_ERR(ptr: soc_dev); |
538 | goto free_soc_dev_attr; |
539 | } |
540 | |
541 | return 0; |
542 | |
543 | free_soc_dev_attr: |
544 | kfree(objp: soc_dev_attr->revision); |
545 | kfree_const(x: soc_dev_attr->soc_id); |
546 | kfree_const(x: soc_dev_attr->family); |
547 | kfree(objp: soc_dev_attr); |
548 | return ret; |
549 | } |
550 | early_initcall(renesas_soc_init); |
551 | |