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