1 // SPDX-License-Identifier: Apache-2.0
2 /* Copyright 2013-2019 IBM Corp. */
3
4 #include <skiboot.h>
5 #include <vpd.h>
6 #include <string.h>
7 #include "spira.h"
8 #include "hdata.h"
9 #include <device.h>
10 #include "hdata.h"
11 #include <inttypes.h>
12 #include <mem_region-malloc.h>
13
14 struct card_info {
15 const char *ccin; /* Customer card identification number */
16 const char *description;
17 };
18
19 static const struct card_info card_table[] = {
20 {"2B06", "System planar 2S4U"},
21 {"2B07", "System planar 1S4U"},
22 {"2B2E", "System planar 2S2U"},
23 {"2B2F", "System planar 1S2U"},
24 {"2CD4", "System planar 2S4U"},
25 {"2CD5", "System planar 1S4U"},
26 {"2CD6", "System planar 2S2U"},
27 {"2CD7", "System planar 1S2U"},
28 {"2CD7", "System planar 1S2U"},
29 {"2B09", "Base JBOD, RAID and Backplane HD"},
30 {"57D7", "Split JBOD, RAID Card"},
31 {"2B0B", "Native I/O Card"},
32
33 /* Anchor cards */
34 {"52FE", "System Anchor Card - IBM Power 824"},
35 {"52F2", "System Anchor Card - IBM Power 814"},
36 {"52F5", "System Anchor Card - IBM Power 822"},
37 {"561A", "System Anchor Card - IBM Power 824L"},
38 {"524D", "System Anchor Card - IBM Power 822L"},
39 {"560F", "System Anchor Card - IBM Power 812L"},
40 {"561C", "System Anchor Card - DS8870"},
41
42 /* Memory DIMMs */
43 {"31E0", "16GB CDIMM"},
44 {"31E8", "16GB CDIMM"},
45 {"31E1", "32GB CDIMM"},
46 {"31E9", "32GB CDIMM"},
47 {"31E2", "64GB CDIMM"},
48 {"31EA", "64GB CDIMM"},
49
50 /* Power supplies */
51 {"2B1D", "Power Supply 900W AC"},
52 {"2B1E", "Power Supply 1400W AC"},
53 {"2B75", "Power Supply 1400W HVDC"},
54
55 /* Fans */
56 {"2B1F", "Fan 4U (A1, A2, A3, A4)"},
57 {"2B29", "Fan 2U (A1, A2, A3, A4, A5, A6)"},
58
59 /* Other cards */
60 };
61
62 struct vpd_key_map {
63 const char *keyword; /* 2 char keyword */
64 const char *description;
65 };
66
67 static const struct vpd_key_map vpd_key_table[] = {
68 {"AA", "ac-power-supply"},
69 {"AM", "air-mover"},
70 {"AV", "anchor-card"},
71
72 {"BA", "bus-adapter-card"},
73 {"BC", "battery-charger"},
74 {"BD", "bus-daughter-card"},
75 {"BE", "bus-expansion-card"},
76 {"BP", "backplane"},
77 {"BR", "backplane-riser"},
78 {"BX", "backplane-extender"},
79
80 {"CA", "calgary-bridge"},
81 {"CB", "infiniband-connector"},
82 {"CC", "clock-card"},
83 {"CD", "card-connector"},
84 {"CE", "ethernet-connector"},
85 {"CL", "calgary-phb"},
86 {"CI", "capacity-card"},
87 {"CO", "sma-connector"},
88 {"CP", "processor-capacity-card"},
89 {"CR", "rio-connector"},
90 {"CS", "serial-connector"},
91 {"CU", "usb-connector"},
92
93 {"DB", "dasd-backplane"},
94 {"DC", "drawer-card"},
95 {"DE", "drawer-extension"},
96 {"DI", "drawer-interposer"},
97 {"DL", "p7ih-dlink-connector"},
98 {"DT", "legacy-pci-card"},
99 {"DV", "media-drawer-led"},
100
101 {"EI", "enclosure-led"},
102 {"EF", "enclosure-fault-led"},
103 {"ES", "embedded-sas"},
104 {"ET", "ethernet-riser"},
105 {"EV", "enclosure"},
106
107 {"FM", "frame"},
108 {"FN", "fru-stocking-part-number"},
109
110 {"HB", "host-rio-pci-card"},
111 {"HD", "high-speed-card"},
112 {"HM", "hmc-connector"},
113
114 {"IB", "io-backplane"},
115 {"IC", "io-card"},
116 {"ID", "ide-connector"},
117 {"II", "io-drawer-led"},
118 {"IP", "interplane-card"},
119 {"IS", "smp-vbus-cable"},
120 {"IT", "enclosure-cable"},
121 {"IV", "io-enclosure"},
122
123 {"KV", "keyboard-led"},
124
125 {"L2", "l2-cache-module"},
126 {"L3", "l3-cache-module"},
127 {"LC", "squadrons-light-connector"},
128 {"LR", "p7ih-connector"},
129 {"LO", "system-locate-led"},
130 {"LT", "squadrons-light-strip"},
131
132 {"MB", "media-backplane"},
133 {"ME", "map-extension"},
134 {"MM", "mip-meter"},
135 {"MS", "ms-dimm"},
136
137 {"NB", "nvram-battery"},
138 {"NC", "sp-node-controller"},
139 {"ND", "numa-dimm"},
140
141 {"OD", "cuod-card"},
142 {"OP", "op-panel"},
143 {"OS", "oscillator"},
144
145 {"P2", "ioc"},
146 {"P5", "ioc-bridge"},
147 {"PB", "io-drawer-backplane"},
148 {"PC", "power-capacitor"},
149 {"PD", "processor-card"},
150 {"PF", "processor"},
151 {"PI", "ioc-phb"},
152 {"PO", "spcn"},
153 {"PN", "spcn-connector"},
154 {"PR", "pci-riser-card"},
155 {"PS", "power-supply"},
156 {"PT", "pass-through-card"},
157 {"PX", "psc-sync-card"},
158 {"PW", "power-connector"},
159
160 {"RG", "regulator"},
161 {"RI", "riser"},
162 {"RK", "rack-indicator"},
163 {"RW", "riscwatch-connector"},
164
165 {"SA", "sys-attn-led"},
166 {"SB", "backup-sysvpd"},
167 {"SC", "scsi-connector"},
168 {"SD", "sas-connector"},
169 {"SI", "scsi-ide-converter"},
170 {"SL", "phb-slot"},
171 {"SN", "smp-cable-connector"},
172 {"SP", "service-processor"},
173 {"SR", "service-card"},
174 {"SS", "soft-switch"},
175 {"SV", "system-vpd"},
176 {"SY", "legacy-sysvpd"},
177
178 {"TD", "tod-clock"},
179 {"TI", "torrent-pcie-phb"},
180 {"TL", "torrent-riser"},
181 {"TM", "thermal-sensor"},
182 {"TP", "tpmd-adapter"},
183 {"TR", "torrent-bridge"},
184
185 {"VV", "root-node-vpd"},
186
187 {"WD", "water_device"},
188 };
189
vpd_map_name(const char * vpd_name)190 static const char *vpd_map_name(const char *vpd_name)
191 {
192 int i;
193
194 for (i = 0; i < ARRAY_SIZE(vpd_key_table); i++)
195 if (!strcmp(vpd_key_table[i].keyword, vpd_name))
196 return vpd_key_table[i].description;
197
198 prlog(PR_WARNING, "VPD: Could not map FRU ID %s to a known name\n",
199 vpd_name);
200
201 return "Unknown";
202 }
203
card_info_lookup(char * ccin)204 static const struct card_info *card_info_lookup(char *ccin)
205 {
206 int i;
207 for(i = 0; i < ARRAY_SIZE(card_table); i++)
208 /* CCIN is always 4 bytes in size */
209 if (!strncmp(card_table[i].ccin, ccin, 4))
210 return &card_table[i];
211 return NULL;
212 }
213
214 /* Discard trailing spaces and populate device tree */
dt_add_prop_sanitize_val(struct dt_node * node,const char * name,const char * val,int vlen)215 static struct dt_property *dt_add_prop_sanitize_val(struct dt_node *node,
216 const char *name, const char *val, int vlen)
217 {
218 char *prop = zalloc(vlen + 1);
219 int i;
220 struct dt_property *p = NULL;
221
222 if (!prop)
223 return p;
224
225 memcpy(prop, val, vlen);
226 for (i = vlen - 1; i >= 0; i--) {
227 if (prop[i] != 0x20) {
228 prop[i + 1] = '\0';
229 break;
230 }
231 }
232
233 if (i >= 0 && !dt_find_property(node, name))
234 p = dt_add_property_string(node, name, prop);
235
236 free(prop);
237 return p;
238 }
239
240 /*
241 * OpenPower system does not provide processor vendor name under FRU VPD.
242 * Parse processor module VPD to get vendor detail
243 */
dt_add_proc_vendor(struct dt_node * proc_node,const void * mvpd,unsigned int mvpd_sz)244 void dt_add_proc_vendor(struct dt_node *proc_node,
245 const void *mvpd, unsigned int mvpd_sz)
246 {
247 const void *kw;
248 uint8_t sz;
249
250 kw = vpd_find(mvpd, mvpd_sz, "VINI", "VN", &sz);
251 if (kw)
252 dt_add_prop_sanitize_val(proc_node, "vendor", kw, sz);
253 }
254
255 /*
256 * For OpenPOWER, we only decipher OPFR records. While OP HDAT have VINI
257 * records too, populating the fields in there is optional. Also, there
258 * is an overlap in the fields contained therein.
259 */
vpd_opfr_parse(struct dt_node * node,const void * fruvpd,unsigned int fruvpd_sz)260 static void vpd_opfr_parse(struct dt_node *node,
261 const void *fruvpd, unsigned int fruvpd_sz)
262 {
263 const void *kw;
264 uint8_t sz;
265
266 /* Vendor Name */
267 kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "VN", &sz);
268 if (kw)
269 dt_add_prop_sanitize_val(node, "vendor", kw, sz);
270
271 /* FRU Description */
272 kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "DR", &sz);
273 if (kw)
274 dt_add_prop_sanitize_val(node, "description", kw, sz);
275
276 /* Part number */
277 kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "VP", &sz);
278 if (kw)
279 dt_add_prop_sanitize_val(node, "part-number", kw, sz);
280
281 /* Serial number */
282 kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "VS", &sz);
283 if (kw)
284 dt_add_prop_sanitize_val(node, "serial-number", kw, sz);
285
286 /* Build date in BCD */
287 kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "MB", &sz);
288 if (kw)
289 dt_add_prop_sanitize_val(node, "build-date", kw, sz);
290
291 return;
292 }
293
294 /*
295 * For CPUs, parse the VRML data.
296 */
vpd_vrml_parse(struct dt_node * node,const void * fruvpd,unsigned int fruvpd_sz)297 static void vpd_vrml_parse(struct dt_node *node,
298 const void *fruvpd, unsigned int fruvpd_sz)
299 {
300 const void *kw;
301 uint8_t sz;
302
303 /* Part number */
304 kw = vpd_find(fruvpd, fruvpd_sz, "VRML", "PN", &sz);
305 if (kw)
306 dt_add_prop_sanitize_val(node, "part-number", kw, sz);
307
308 /* Serial number */
309 kw = vpd_find(fruvpd, fruvpd_sz, "VRML", "SN", &sz);
310 if (kw)
311 dt_add_prop_sanitize_val(node, "serial-number", kw, sz);
312
313 return;
314 }
315
vpd_vini_parse(struct dt_node * node,const void * fruvpd,unsigned int fruvpd_sz)316 static void vpd_vini_parse(struct dt_node *node,
317 const void *fruvpd, unsigned int fruvpd_sz)
318 {
319 const void *kw;
320 const char *desc;
321 uint8_t sz;
322 const struct card_info *cinfo;
323
324 /* FRU Stocking Part Number */
325 kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "FN", &sz);
326 if (kw)
327 dt_add_prop_sanitize_val(node, "fru-number", kw, sz);
328
329 /* Serial Number */
330 kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "SN", &sz);
331 if (kw)
332 dt_add_prop_sanitize_val(node, "serial-number", kw, sz);
333
334 /* Part Number */
335 kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "PN", &sz);
336 if (kw)
337 dt_add_prop_sanitize_val(node, "part-number", kw, sz);
338
339 /* Vendor Name */
340 kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "VN", &sz);
341 if (kw)
342 dt_add_prop_sanitize_val(node, "vendor", kw, sz);
343
344 /* CCIN Extension */
345 kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CE", &sz);
346 if (kw)
347 dt_add_prop_sanitize_val(node, "ccin-extension", kw, sz);
348
349 /* HW Version info */
350 kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "HW", &sz);
351 if (kw)
352 dt_add_prop_sanitize_val(node, "hw-version", kw, sz);
353
354 /* Card type info */
355 kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CT", &sz);
356 if (kw)
357 dt_add_prop_sanitize_val(node, "card-type", kw, sz);
358
359 /* HW characteristics info */
360 kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "B3", &sz);
361 if (kw)
362 dt_add_prop_sanitize_val(node, "hw-characteristics", kw, sz);
363
364 /* Customer Card Identification Number (CCIN) */
365 kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CC", &sz);
366 if (kw) {
367 dt_add_prop_sanitize_val(node, "ccin", kw, sz);
368
369 cinfo = card_info_lookup((char *)kw);
370 if (cinfo) {
371 dt_add_property_string(node,
372 "description", cinfo->description);
373 } else {
374 desc = vpd_find(fruvpd, fruvpd_sz, "VINI", "DR", &sz);
375 if (desc) {
376 dt_add_prop_sanitize_val(node,
377 "description", desc, sz);
378 } else {
379 dt_add_property_string(node, "description", "Unknown");
380 prlog(PR_WARNING,
381 "VPD: CCIN desc not available for: %s\n",
382 (char*)kw);
383 }
384 }
385 }
386
387 return;
388 }
389
valid_child_entry(const struct slca_entry * entry)390 static bool valid_child_entry(const struct slca_entry *entry)
391 {
392 if ((entry->install_indic == SLCA_INSTALL_INSTALLED) &&
393 (entry->vpd_collected == SLCA_VPD_COLLECTED))
394 return true;
395
396 return false;
397 }
398
vpd_data_parse(struct dt_node * node,const void * fruvpd,u32 fruvpd_sz)399 void vpd_data_parse(struct dt_node *node, const void *fruvpd, u32 fruvpd_sz)
400 {
401 if (vpd_find_record(fruvpd, fruvpd_sz, "OPFR", NULL))
402 vpd_opfr_parse(node, fruvpd, fruvpd_sz);
403 else if (vpd_find_record(fruvpd, fruvpd_sz, "VRML", NULL))
404 vpd_vrml_parse(node, fruvpd, fruvpd_sz);
405 else
406 vpd_vini_parse(node, fruvpd, fruvpd_sz);
407 }
408
409 /* Create the /vpd node and add its children */
dt_init_vpd_node(void)410 void dt_init_vpd_node(void)
411 {
412 const char *name, *p_name;
413 int count, index;
414 uint64_t addr, p_addr;
415 struct dt_node *dt_vpd;
416 struct HDIF_common_hdr *slca_hdr;
417 struct dt_node *parent, *node;
418 const struct slca_entry *entry, *p_entry;
419
420 dt_vpd = dt_new(dt_root, "vpd");
421 assert(dt_vpd);
422 dt_add_property_string(dt_vpd, "compatible", "ibm,opal-v3-vpd");
423 dt_add_property_cells(dt_vpd, "#size-cells", 0);
424 dt_add_property_cells(dt_vpd, "#address-cells", 1);
425
426 slca_hdr = get_hdif(&spira.ntuples.slca, SLCA_HDIF_SIG);
427 if (!slca_hdr) {
428 prerror("SLCA Invalid\n");
429 return;
430 }
431
432 count = HDIF_get_iarray_size(slca_hdr, SLCA_IDATA_ARRAY);
433 if (count < 0) {
434 prerror("SLCA: Can't find SLCA array size!\n");
435 return;
436 }
437
438 for (index = 0; index < count; index++) {
439 /* Get SLCA entry */
440 entry = slca_get_entry(index);
441 if (!entry)
442 continue;
443
444 /*
445 * A child entry is valid if all of the following criteria is met
446 * a. SLCA_INSTALL_INSTALLED is set in s_entry->install_indic
447 * b. SLCA_VPD_COLLECTED is set in s_entry->vpd_collected
448 * c. The SLCA is not a duplicate entry.
449 */
450 if (!valid_child_entry(entry))
451 goto next_entry;
452
453 name = vpd_map_name(entry->fru_id);
454 addr = be16_to_cpu(entry->rsrc_id);
455 /* Check node is already created or not */
456 if (dt_find_by_name_addr(dt_vpd, name, addr))
457 goto next_entry;
458
459 /* Get parent node */
460 if (index == SLCA_ROOT_INDEX) {
461 parent = dt_vpd;
462 } else {
463 p_entry = slca_get_entry(be16_to_cpu(entry->parent_index));
464 if (!p_entry)
465 goto next_entry;
466 p_name = vpd_map_name(p_entry->fru_id);
467 p_addr = be16_to_cpu(p_entry->rsrc_id);
468 parent = dt_find_by_name_addr(dt_vpd, p_name, p_addr);
469 }
470 if (!parent)
471 goto next_entry;
472
473 node = dt_new_addr(parent, name, addr);
474 if (!node) {
475 prerror("VPD: Creating node at %s@%"PRIx64" failed\n",
476 name, addr);
477 goto next_entry;
478 }
479
480 /* Add location code */
481 slca_vpd_add_loc_code(node, be16_to_cpu(entry->my_index));
482 /* Add FRU label */
483 dt_add_property(node, "fru-type", entry->fru_id, 2);
484 dt_add_property_cells(node, "reg", addr);
485 dt_add_property_cells(node, "#size-cells", 0);
486 dt_add_property_cells(node, "#address-cells", 1);
487
488 next_entry:
489 /* Skip dups -- dups are contiguous */
490 index += entry->nr_dups;
491 }
492 }
493
dt_add_vpd_node(const struct HDIF_common_hdr * hdr,int indx_fru,int indx_vpd)494 struct dt_node *dt_add_vpd_node(const struct HDIF_common_hdr *hdr,
495 int indx_fru, int indx_vpd)
496 {
497 const struct spira_fru_id *fru_id;
498 unsigned int fruvpd_sz, fru_id_sz;
499 const struct slca_entry *entry;
500 struct dt_node *dt_vpd, *node;
501 const void *fruvpd;
502 const char *name;
503 uint64_t addr;
504
505 fru_id = HDIF_get_idata(hdr, indx_fru, &fru_id_sz);
506 if (!fru_id)
507 return NULL;
508
509 fruvpd = HDIF_get_idata(hdr, indx_vpd, &fruvpd_sz);
510 if (!CHECK_SPPTR(fruvpd))
511 return NULL;
512
513 dt_vpd = dt_find_by_path(dt_root, "/vpd");
514 if (!dt_vpd)
515 return NULL;
516
517 entry = slca_get_entry(be16_to_cpu(fru_id->slca_index));
518 if (!entry)
519 return NULL;
520
521 name = vpd_map_name(entry->fru_id);
522 addr = be16_to_cpu(entry->rsrc_id);
523 /* Get the node already created */
524 node = dt_find_by_name_addr(dt_vpd, name, addr);
525 /*
526 * It is unlikely that node not found because vpd nodes have the
527 * corresponding slca entry which we would have used to populate the vpd
528 * tree during the 'first' pass above so that we just need to perform
529 * VINI parse and add the vpd data..
530 */
531 if (!node)
532 return NULL;
533
534 /* Parse VPD fields, ensure that it has not been added already */
535 if (vpd_valid(fruvpd, fruvpd_sz)
536 && !dt_find_property(node, "ibm,vpd")) {
537 dt_add_property(node, "ibm,vpd", fruvpd, fruvpd_sz);
538 vpd_data_parse(node, fruvpd, fruvpd_sz);
539 }
540
541 return node;
542 }
543
dt_add_model_name(void)544 static void dt_add_model_name(void)
545 {
546 const char *model_name = NULL;
547 const struct machine_info *mi;
548 const struct iplparams_sysparams *p;
549 const struct HDIF_common_hdr *iplp;
550 const struct dt_property *model;
551
552 model = dt_find_property(dt_root, "model");
553
554 iplp = get_hdif(&spira.ntuples.ipl_parms, "IPLPMS");
555 if (!iplp)
556 goto def_model;
557
558 p = HDIF_get_idata(iplp, IPLPARAMS_SYSPARAMS, NULL);
559 if (!CHECK_SPPTR(p))
560 goto def_model;
561
562 if (be16_to_cpu(iplp->version) >= 0x60)
563 model_name = p->sys_type_str;
564
565 def_model:
566 if ((!model_name || model_name[0] == '\0') && model) {
567 mi = machine_info_lookup(model->prop);
568 if (mi)
569 model_name = mi->name;
570 }
571
572 if(model_name)
573 dt_add_property_string(dt_root, "model-name", model_name);
574 }
575
sysvpd_parse_opp(const void * sysvpd,unsigned int sysvpd_sz)576 static void sysvpd_parse_opp(const void *sysvpd, unsigned int sysvpd_sz)
577 {
578 const char *v;
579 uint8_t sz;
580
581 v = vpd_find(sysvpd, sysvpd_sz, "OSYS", "MM", &sz);
582 if (v)
583 dt_add_prop_sanitize_val(dt_root, "model", v, sz);
584 else
585 dt_add_property_string(dt_root, "model", "Unknown");
586
587 v = vpd_find(sysvpd, sysvpd_sz, "OSYS", "SS", &sz);
588 if (v)
589 dt_add_prop_sanitize_val(dt_root, "system-id", v, sz);
590 else
591 dt_add_property_string(dt_root, "system-id", "Unknown");
592 }
593
594
sysvpd_parse_legacy(const void * sysvpd,unsigned int sysvpd_sz)595 static void sysvpd_parse_legacy(const void *sysvpd, unsigned int sysvpd_sz)
596 {
597 const char *model;
598 const char *system_id;
599 const char *brand;
600 uint8_t sz;
601
602 model = vpd_find(sysvpd, sysvpd_sz, "VSYS", "TM", &sz);
603 if (model)
604 dt_add_prop_sanitize_val(dt_root, "model", model, sz);
605 else
606 dt_add_property_string(dt_root, "model", "Unknown");
607
608 system_id = vpd_find(sysvpd, sysvpd_sz, "VSYS", "SE", &sz);
609 if (system_id)
610 dt_add_prop_sanitize_val(dt_root, "system-id", system_id, sz);
611 else
612 dt_add_property_string(dt_root, "system-id", "Unknown");
613
614 brand = vpd_find(sysvpd, sysvpd_sz, "VSYS", "BR", &sz);
615 if (brand)
616 dt_add_prop_sanitize_val(dt_root, "system-brand", brand, sz);
617 else
618 dt_add_property_string(dt_root, "brand", "Unknown");
619 }
620
sysvpd_parse(void)621 static void sysvpd_parse(void)
622 {
623 const void *sysvpd;
624 unsigned int sysvpd_sz;
625 unsigned int fru_id_sz;
626 struct dt_node *dt_vpd;
627 const struct spira_fru_id *fru_id;
628 struct HDIF_common_hdr *sysvpd_hdr;
629
630 sysvpd_hdr = get_hdif(&spira.ntuples.system_vpd, SYSVPD_HDIF_SIG);
631 if (!sysvpd_hdr)
632 return;
633
634 fru_id = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_FRU_ID, &fru_id_sz);
635 if (!fru_id)
636 return;
637
638 sysvpd = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_KW_VPD, &sysvpd_sz);
639 if (!CHECK_SPPTR(sysvpd))
640 return;
641
642 /* Add system VPD */
643 dt_vpd = dt_find_by_path(dt_root, "/vpd");
644 if (dt_vpd) {
645 dt_add_property(dt_vpd, "ibm,vpd", sysvpd, sysvpd_sz);
646 slca_vpd_add_loc_code(dt_vpd, be16_to_cpu(fru_id->slca_index));
647 }
648
649 /* Look for the new OpenPower "OSYS" first */
650 if (vpd_find_record(sysvpd, sysvpd_sz, "OSYS", NULL))
651 sysvpd_parse_opp(sysvpd, sysvpd_sz);
652 else
653 sysvpd_parse_legacy(sysvpd, sysvpd_sz);
654
655 dt_add_model_name();
656 }
657
iokid_vpd_parse(const struct HDIF_common_hdr * iohub_hdr)658 static void iokid_vpd_parse(const struct HDIF_common_hdr *iohub_hdr)
659 {
660 const struct HDIF_child_ptr *iokids;
661 const struct HDIF_common_hdr *iokid;
662 unsigned int i;
663
664 iokids = HDIF_child_arr(iohub_hdr, CECHUB_CHILD_IO_KIDS);
665 if (!CHECK_SPPTR(iokids)) {
666 prerror("VPD: No IOKID child array\n");
667 return;
668 }
669
670 for (i = 0; i < be32_to_cpu(iokids->count); i++) {
671 iokid = HDIF_child(iohub_hdr, iokids, i,
672 IOKID_FRU_HDIF_SIG);
673 /* IO KID VPD structure layout is similar to FRUVPD */
674 if (iokid)
675 dt_add_vpd_node(iokid,
676 FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD);
677 }
678 }
679
iohub_vpd_parse(void)680 static void iohub_vpd_parse(void)
681 {
682 const struct HDIF_common_hdr *iohub_hdr;
683 const struct cechub_hub_fru_id *fru_id_data;
684 unsigned int i, vpd_sz, fru_id_sz;
685
686 if (!get_hdif(&spira.ntuples.cec_iohub_fru, CECHUB_FRU_HDIF_SIG)) {
687 prerror("VPD: Could not find IO HUB FRU data\n");
688 return;
689 }
690
691 for_each_ntuple_idx(&spira.ntuples.cec_iohub_fru, iohub_hdr,
692 i, CECHUB_FRU_HDIF_SIG) {
693
694 fru_id_data = HDIF_get_idata(iohub_hdr,
695 CECHUB_FRU_ID_DATA_AREA,
696 &fru_id_sz);
697
698 /* P8, IO HUB is on processor card and we have a
699 * daughter card array
700 */
701 if (fru_id_data &&
702 be32_to_cpu(fru_id_data->card_type) == CECHUB_FRU_TYPE_CPU_CARD) {
703 iokid_vpd_parse(iohub_hdr);
704 continue;
705 }
706
707 if (HDIF_get_idata(iohub_hdr,
708 CECHUB_ASCII_KEYWORD_VPD, &vpd_sz))
709 dt_add_vpd_node(iohub_hdr, CECHUB_FRU_ID_DATA,
710 CECHUB_ASCII_KEYWORD_VPD);
711 }
712 }
713
_vpd_parse(struct spira_ntuple tuple)714 static void _vpd_parse(struct spira_ntuple tuple)
715 {
716 const struct HDIF_common_hdr *fruvpd_hdr;
717 unsigned int i;
718
719 if (!get_hdif(&tuple, FRUVPD_HDIF_SIG))
720 return;
721
722 for_each_ntuple_idx(&tuple, fruvpd_hdr, i, FRUVPD_HDIF_SIG)
723 dt_add_vpd_node(fruvpd_hdr,
724 FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD);
725 }
726
vpd_parse(void)727 void vpd_parse(void)
728 {
729 const struct HDIF_common_hdr *fruvpd_hdr;
730
731 /* System VPD uses the VSYS record, so its special */
732 sysvpd_parse();
733
734 /* Enclosure */
735 _vpd_parse(spira.ntuples.nt_enclosure_vpd);
736
737 /* Backplane */
738 _vpd_parse(spira.ntuples.backplane_vpd);
739
740 /* clock card -- does this use the FRUVPD sig? */
741 _vpd_parse(spira.ntuples.clock_vpd);
742
743 /* Anchor card */
744 _vpd_parse(spira.ntuples.anchor_vpd);
745
746 /* Op panel -- does this use the FRUVPD sig? */
747 _vpd_parse(spira.ntuples.op_panel_vpd);
748
749 /* External cache FRU vpd -- does this use the FRUVPD sig? */
750 _vpd_parse(spira.ntuples.ext_cache_fru_vpd);
751
752 /* Misc CEC FRU */
753 _vpd_parse(spira.ntuples.misc_cec_fru_vpd);
754
755 /* CEC IO HUB FRU */
756 iohub_vpd_parse();
757
758 /*
759 * SP subsystem -- while the rest of the SPINFO structure is
760 * different, the FRU ID data and pointer pair to keyword VPD
761 * are the same offset as a FRUVPD entry. So reuse it
762 */
763 fruvpd_hdr = get_hdif(&spira.ntuples.sp_subsys, SPSS_HDIF_SIG);
764 if (fruvpd_hdr)
765 dt_add_vpd_node(fruvpd_hdr,
766 FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD);
767 }
768