blob: e42eab0a287c6e6103589dd94c91c9144cca7533 [file] [log] [blame]
Matt Spinler7d381e12016-09-27 14:27:24 -05001#!/usr/bin/env perl
2
3#Generates a BMC device tree syntax file from the machine
4#readable workbook.
5
6use strict;
7use XML::Simple;
8use mrw::Targets;
9use Getopt::Long;
10
11use constant VERSION => "/dts-v1/;";
12use constant STANDALONE_PROPERTY => "standalone_property";
13use constant DTSI_INCLUDE => "DTSI_INCLUDE";
14
15my $serverwizFile;
16my $outputFile;
17my $debug;
18
19GetOptions("x=s" => \$serverwizFile,
20 "o=s" => \$outputFile,
21 "d" => \$debug)
22or printUsage();
23
24if ((not defined $serverwizFile) || (not defined $outputFile)) {
25 printUsage();
26}
27
28my $g_targetObj = Targets->new;
29$g_targetObj->loadXML($serverwizFile);
30
31my $g_bmc = getBMCTarget();
32if (length($g_bmc) == 0) {
33 die "Unable to find a BMC in this system\n";
34}
35
36my $g_bmcModel = $g_targetObj->getAttribute($g_bmc, "MODEL");
37my $g_bmcMfgr = $g_targetObj->getAttribute($g_bmc, "MANUFACTURER");
38my $g_systemName = $g_targetObj->getSystemName();
39
40open (my $f, ">$outputFile") or die "Could not open $outputFile\n";
41
42printVersion($f);
43printIncludes($f, 0);
44printRootNodeStart($f);
45
46printPropertyList($f, 1, "model", getSystemBMCModel());
Matt Spinler7d381e12016-09-27 14:27:24 -050047printPropertyList($f, 1, "compatible", getBMCCompatibles());
Matt Spinler995f2a22016-09-30 13:07:31 -050048
Matt Spinler23d47c22016-10-04 12:31:21 -050049printNode($f, 1, "aliases", getAliases());
Matt Spinler7d381e12016-09-27 14:27:24 -050050printNode($f, 1, "chosen", getChosen());
51printNode($f, 1, "memory", getMemory($g_bmc));
52
Matt Spinler995f2a22016-09-30 13:07:31 -050053printNodes($f, 1, getSpiFlashNodes());
54
55printNode($f, 1, "leds", getLEDNode());
56
Matt Spinler7d381e12016-09-27 14:27:24 -050057printRootNodeEnd($f, 0);
58
59printNodes($f, 0, getMacNodes());
Matt Spinler995f2a22016-09-30 13:07:31 -050060printNodes($f, 0, getUARTNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050061printNodes($f, 0, getVuartNodes());
62
63close $f;
64exit 0;
65
66
Matt Spinler23d47c22016-10-04 12:31:21 -050067#Returns a hash that represents the 'aliases' node.
68#Will look like:
69# aliases {
70# name1 = &val1;
71# name2 = &val2;
72# ...
73# }
74sub getAliases()
75{
76 my %aliases;
77 my $name, my $val;
78
79 #The MRW supports up to 6 name and value pairs.
80 for (my $i = 1; $i <= 6; $i++) {
81 my $nameAttr = "name$i";
82 my $valAttr = "value$i";
83
84 $name = $g_targetObj->getAttributeField($g_bmc, "BMC_DT_ALIASES",
85 $nameAttr);
86 if ($name ne "") {
87 $val = $g_targetObj->getAttributeField($g_bmc, "BMC_DT_ALIASES",
88 $valAttr);
89 #The value will be printed as '&val'
90 $aliases{$name} = "(alias)$val";
91 }
92 }
93
94 return %aliases;
95}
96
Matt Spinler7d381e12016-09-27 14:27:24 -050097
98#Return a hash that represents the 'chosen' node
Matt Spinler995f2a22016-09-30 13:07:31 -050099#Will look like:
100# chosen {
101# stdout-path = ...
102# bootargs = ...
103# }
Matt Spinler7d381e12016-09-27 14:27:24 -0500104sub getChosen()
105{
106 my $bmcStdOut = $g_targetObj->getAttributeField($g_bmc, "BMC_DT_CHOSEN",
107 "stdout-path");
108 my $args = $g_targetObj->getAttributeField($g_bmc, "BMC_DT_CHOSEN",
109 "bootargs");
110 my %chosen;
111 $chosen{"stdout-path"} = $bmcStdOut;
112 $chosen{"bootargs"} = $args;
113 return %chosen;
114}
115
116
Matt Spinler995f2a22016-09-30 13:07:31 -0500117#Gets the nodes that represents the BMC's SPI flash chips. They're based
118#on information from the spi-master-unit end of the connection, with
119#a subnode of information from the destination chip.
120#On ASPEED chips, they're nested under the ahb node (Advanced
121#High-performance Bus).
122#Will look like:
123# ahb {
124# fmc@... {
125# reg = ...
126# #address-cells = ...
127# #size-cells = ...
128# #compatible = ...
129#
130# flash@... {
131# reg = ...
132# compatible = ...
133# label = ...
134# #include ...
135# }
136# }
137# spi@... {
138# ...
139# }
140# }
141sub getSpiFlashNodes()
Matt Spinler7d381e12016-09-27 14:27:24 -0500142{
Matt Spinler995f2a22016-09-30 13:07:31 -0500143 my %parentNode, my %node, my @nodes;
144 my $lastParentNodeName = "default";
145 my $parentNodeName = "ahb";
Matt Spinler7d381e12016-09-27 14:27:24 -0500146
Matt Spinler995f2a22016-09-30 13:07:31 -0500147 my $connections = findConnections($g_bmc, "SPI", "FLASH");
148 if ($connections eq "") {
149 print "WARNING: No SPI flashes found connected to the BMC\n";
150 return @nodes;
151 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500152
Matt Spinler995f2a22016-09-30 13:07:31 -0500153 foreach my $spi (@{$connections->{CONN}}) {
Matt Spinler7d381e12016-09-27 14:27:24 -0500154
Matt Spinler995f2a22016-09-30 13:07:31 -0500155 my %unitNode; #Node for the SPI master unit
156 my %flashNode; #subnode for the flash chip itself
157 my $flashNodeName = "flash";
158 my $nodeLabel = "";
159 my @addresses;
Matt Spinler7d381e12016-09-27 14:27:24 -0500160
Matt Spinler7d381e12016-09-27 14:27:24 -0500161
Matt Spinler995f2a22016-09-30 13:07:31 -0500162 #Adds a comment into the output file about the MRW connection
163 #that makes up this node. Not that {SOURCE} always represents
164 #the master unit, and DEST_PARENT represents the destination
165 #chip. The destination unit {DEST} isn't usually that interesting.
166 $unitNode{COMMENT} = "$spi->{SOURCE} ->\n$spi->{DEST_PARENT}";
167
168 #These flashes are nested in the 'ahb' (an internal chip bus)
169 #node in ASPEED chips. Get the name of it here. Will default
170 #to 'ahb' if not set.
171 if (!$g_targetObj->isBadAttribute($spi->{SOURCE},
172 "INTERNAL_BUS", "NA")) {
173 $parentNodeName = $g_targetObj->getAttribute($spi->{SOURCE},
174 "INTERNAL_BUS");
175 #Not going to support this unless we have to
176 if ($parentNodeName != $lastParentNodeName) {
177 die "ERROR: SPI master unit $spi->{SOURCE} has a " .
178 "different internal bus name $parentNodeName than " .
179 "previous name $lastParentNodeName\n";
180 }
181 else {
182 $lastParentNodeName = $parentNodeName;
Matt Spinler7d381e12016-09-27 14:27:24 -0500183 }
184 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500185 else {
186 print "WARNING: No INTERNAL_BUS attribute value found for " .
187 "SPI flash unit $spi->{SOURCE}. Using '$parentNodeName'\n";
188 }
189
190 #The reg base and size of the unit will be added into
191 #the reg property
192 my $regBase = $g_targetObj->getAttribute($spi->{SOURCE},
193 "BMC_DT_REG_BASE");
194 my $regSize = $g_targetObj->getAttribute($spi->{SOURCE},
195 "BMC_DT_REG_SIZE");
196
197 #There is also another memory range that goes into reg
198 my %sourceRegHash = getMemory($spi->{SOURCE});
199
200 #Insert the regBase and regSize to the memory < ... > property
201 $unitNode{reg} = "< $regBase $regSize " . substr($sourceRegHash{reg}, 2);
202
203 #usually, this will be something like 'smc' or 'spi'
204 my $nodeName = "spi";
205 if (!$g_targetObj->isBadAttribute($spi->{SOURCE},
206 "BMC_DT_NODE_NAME")) {
207 $nodeName = $g_targetObj->getAttribute($spi->{SOURCE},
208 "BMC_DT_NODE_NAME");
209 }
210 else {
211 print "WARNING: No BMC_DT_NODE_NAME attribute value found for " .
212 "SPI flash unit $spi->{SOURCE}. Using 'spi'\n";
213 }
214
215 #now turn it into something like fmc@...
Matt Spinler23d47c22016-10-04 12:31:21 -0500216 $regBase =~ s/^0x//;
Matt Spinler995f2a22016-09-30 13:07:31 -0500217 $nodeName .= "@".$regBase;
218
219 if (!$g_targetObj->isBadAttribute($spi->{SOURCE},
220 "BMC_DT_COMPATIBLE")) {
221 $unitNode{compatible} = $g_targetObj->
222 getAttribute($spi->{SOURCE}, "BMC_DT_COMPATIBLE");
223 }
224 else {
225 print "WARNING: No BMC_DT_COMPATIBLE attribute found for SPI " .
226 "flash unit $spi->{SOURCE}\n";
227 }
228
229 #The flash chip has its one reg property as well
230 if (!$g_targetObj->isBadAttribute($spi->{DEST_PARENT},
231 "BMC_DT_REG_PROPERTY")) {
232 $flashNode{reg} = $g_targetObj->getAttribute($spi->{DEST_PARENT},
233 "BMC_DT_REG_PROPERTY");
234 $flashNode{reg} = "<" . $flashNode{reg} . ">";
235 }
236 else {
237 print "WARNING: No BMC_REG_PROPERTY attribute found for SPI " .
238 "flash $spi->{DEST_PARENT}. Using <0>.\n";
239 $flashNode{reg} = "<0>";
240 }
241
242 if (!$g_targetObj->isBadAttribute($spi->{DEST_PARENT},
243 "BMC_DT_COMPATIBLE")) {
244 $flashNode{compatible} = $g_targetObj->
245 getAttribute($spi->{DEST_PARENT}, "BMC_DT_COMPATIBLE");
246 }
247 else {
248 print "WARNING: No BMC_DT_COMPATIBLE attribute found for SPI " .
249 "flash $spi->{DEST_PARENT}\n";
250 }
251
252 if (!$g_targetObj->isBadAttribute($spi->{DEST_PARENT},
253 "BMC_DT_LABEL_PROPERTY")) {
254 $flashNode{label} = $g_targetObj->
255 getAttribute($spi->{DEST_PARENT}, "BMC_DT_LABEL_PROPERTY");
256 }
257
258 #Some flash chips have a .dtsi include to pull in more properties.
259 #Future - contents of the includes could be pulled into the MRW
260 #as new attributes.
261 if (!$g_targetObj->isBadAttribute($spi->{DEST_PARENT},
262 "BMC_DT_INCLUDES")) {
263 my $incs = $g_targetObj->
264 getAttribute($spi->{DEST_PARENT}, "BMC_DT_INCLUDES");
265 #first remove the spaces and NAs
266 $incs =~ s/\s+//g;
267 $incs =~ s/NA,*//g;
268 $flashNode{DTSI_INCLUDE} = $incs;
269 }
270
271 #the flash subnode name also has its reg[0] appended
272 #like flash@...
273 @addresses = split(' ', $flashNode{reg});
274 $addresses[0] =~ s/<//;
275 $addresses[0] =~ s/>//;
276 $flashNodeName .= "@" . $addresses[0];
277 $unitNode{$flashNodeName} = { %flashNode };
278
279 #For now, just support a chip with 1 reg value
280 if (scalar @addresses == 1) {
281 $unitNode{'#address-cells'} = "<1>";
282 $unitNode{'#size-cells'} = "<0>";
283 }
284 else {
285 die "ERROR: Unsupported number of <reg> entries " .
286 "in flash node $flashNodeName for SPI flash " .
287 "$spi->{DEST_PARENT}. Only 1 entry supported.\n";
288 }
289
290 #This node will end up being in an array on the parent node
291 my %node;
292 $node{$nodeName} = { %unitNode };
293 push @nodes, { %node };
Matt Spinler7d381e12016-09-27 14:27:24 -0500294 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500295
296 $parentNode{$parentNodeName}{nodes} = [ @nodes ];
297
298 #There is always just one in the array
299 my @finalNodes;
300 push @finalNodes, { %parentNode };
301 return @finalNodes;
302}
303
304
305#Returns a hash that represents the leds node by finding all of the
306#GPIO connections to LEDs.
307#Node will look like:
308# leds {
309# <ledname> {
310# gpios = &gpio ASPEED_GPIO(x, y) GPIO_ACTIVE_xxx>
311# };
312# <another ledname> {
313# ...
314# }
315sub getLEDNode()
316{
317 my %leds;
318
319 $leds{compatible} = "gpio-led";
320
321 my $connections = findConnections($g_bmc, "GPIO", "LED");
322
323 if ($connections eq "") {
324 print "WARNING: No LEDs found connected to the BMC\n";
325 return %leds;
326 }
327
328 foreach my $gpio (@{$connections->{CONN}}) {
329 my %ledNode;
330
331 $ledNode{COMMENT} = "$gpio->{SOURCE} ->\n$gpio->{DEST_PARENT}";
332
333 #The node name will be the simplified LED name
334 my $name = $gpio->{DEST_PARENT};
335 $name =~ s/(-\d+$)//; #remove trailing position
336 $name =~ s/.*\///; #remove the front of the path
337
338 #For now only supports ASPEED.
339 if (uc($g_bmcMfgr) ne "ASPEED") {
340 die "ERROR: Unsupported BMC manufacturer $g_bmcMfgr\n";
341 }
342 my $num = $g_targetObj->getAttribute($gpio->{SOURCE}, "PIN_NUM");
343 my $macro = getAspeedGpioMacro($num);
344
345 #If it's active high or low
346 my $state = $g_targetObj->getAttribute($gpio->{DEST_PARENT}, "ON_STATE");
347 my $activeString = getGpioActiveString($state);
348
349 $ledNode{gpios} = "<&gpio $macro $activeString>";
350
351 $leds{$name} = { %ledNode };
352 }
353
354 return %leds;
355}
356
357
358#Returns a either GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW
359# $val = either a 1 or a 0 for active high or low
360sub getGpioActiveString() {
361 my $val = shift;
362
363 if ($val == 0) {
364 return "GPIO_ACTIVE_LOW";
365 }
366
367 return "GPIO_ACTIVE_HIGH";
368}
369
370
371#Turns a GPIO number into something like ASPEED_GPIO(A, 0) for the
372#ASPEED GPIO numbering scheme A[0-7] -> Z[0-7] and then starts at
373#AA[0-7] after that.
374# $num = the GPIO number
375sub getAspeedGpioMacro() {
376 my $num = shift;
377 my $char;
378 my $offset = $num % 8;
379 my $block = int($num / 8);
380
381 #If past Z, wraps to AA, AB, etc
382 if ((ord('A') + $block) > ord('Z')) {
383 #how far past Z?
384 $char = $block - (ord('Z') - ord('A'));
385
386 #Don't let it wrap twice
387 if ($char > (ord('Z') - ord('A') + 1)) {
388 die "ERROR: Invalid PIN_NUM value $num found for GPIO\n";
389 }
390
391 #start back at 'A' again, and convert to a character
392 $char = chr($char + ord('A') - 1);
393
394 #Add in a bonus 'A', to get something like AB
395 $char = "A".$char;
396 }
397 else {
398 $char = ord('A') + $block;
399 $char = chr($char);
400 }
401
402 return "ASPEED_GPIO($char, $offset)";
403}
404
405
406#Returns a list of hashes that represent the UART nodes on the BMC by
407#finding the UART connections.
408#Nodes will look like:
409# &uartX {
410# status = "okay"
411# }
412sub getUARTNodes()
413{
414 my @nodes;
415
Matt Spinler23d47c22016-10-04 12:31:21 -0500416 #Using U750 for legacy MRW reasons
417 my $connections = findConnections($g_bmc, "U750");
Matt Spinler995f2a22016-09-30 13:07:31 -0500418
419 if ($connections eq "") {
420 print "WARNING: No UART buses found connected to the BMC\n";
421 return @nodes;
422 }
423
424 foreach my $uart (@{$connections->{CONN}}) {
425 my %node;
426
427 my $num = $g_targetObj->getAttribute($uart->{SOURCE}, "CHIP_UNIT");
428 my $name = "uart$num";
429
430 $node{$name}{status} = "okay";
431 $node{$name}{COMMENT} = "$uart->{SOURCE} ->\n$uart->{DEST_PARENT}";
432
433 push @nodes, { %node };
434 }
435
Matt Spinler7d381e12016-09-27 14:27:24 -0500436 return @nodes;
437}
438
439
Matt Spinler995f2a22016-09-30 13:07:31 -0500440#Returns a list of hashes that represent the MAC (ethernet) nodes on the BMC
441#by finding the connections of type ETHERNET.
442#Nodes will look like:
443# &macX {
444# ...
445# }
446sub getMacNodes()
447{
448 my @nodes;
449
450 my $connections = findConnections($g_bmc, "ETHERNET");
451
452 if ($connections eq "") {
453 print "WARNING: No ethernet buses found connected to the BMC\n";
454 return @nodes;
455 }
456
457 foreach my $eth (@{$connections->{CONN}}) {
458 my %node;
459
460 my $num = $g_targetObj->getAttribute($eth->{SOURCE}, "CHIP_UNIT");
461 my $ncsi = $g_targetObj->getAttribute($eth->{SOURCE}, "NCSI_MODE");
462 my $hwChecksum = $g_targetObj->getAttribute($eth->{SOURCE},
463 "USE_HW_CHECKSUM");
464
465 my $name = "mac$num";
466 $node{$name}{status} = "okay";
467
468 if ($ncsi == 1) {
469 $node{$name}{"use-ncsi"} = STANDALONE_PROPERTY;
470 }
471 if ($hwChecksum == 0) {
472 $node{$name}{"no-hw-checksum"} = STANDALONE_PROPERTY;
473 }
474
475 $node{$name}{COMMENT} = "$eth->{SOURCE} ->\n$eth->{DEST_PARENT}";
476
477 push @nodes, { %node };
478 }
479
480 return @nodes;
481}
482
483
484#Returns a list of hashes that represent the virtual UART nodes
485#Node will look like:
486# &vuart {
487# status = "okay"
488# }
Matt Spinler7d381e12016-09-27 14:27:24 -0500489sub getVuartNodes()
490{
491 my @nodes;
492 my %node;
493
494 #For now, enable 1 node all the time.
Matt Spinler995f2a22016-09-30 13:07:31 -0500495 #TBD if this needs to be fixed
Matt Spinler7d381e12016-09-27 14:27:24 -0500496 $node{vuart}{status} = "okay";
497
498 push @nodes, { %node };
499
500 return @nodes;
501}
502
503
504#Returns a hash{'reg'} = "<.....>" based on the
505#BMC_DT_MEMORY attribute. This is used to display
506#memory ranges.
507sub getMemory()
508{
509 my $target = shift;
510 my $memory = $g_targetObj->getAttribute($target, "BMC_DT_MEMORY");
511 my @mem = split(',', $memory);
512 my %property;
Matt Spinler995f2a22016-09-30 13:07:31 -0500513 my $val = "< ";
Matt Spinler7d381e12016-09-27 14:27:24 -0500514
515 #Encoded as 4 <base address>,<size> pairs of memory ranges
516 #Unused ranges are all 0s.
517 #For now, assumes 32 bit numbers, revisit later for 64 bit support
518 #Convert it into: <num1 num2 num3 num4 etc>
519
520 for (my $i = 0;$i < scalar @mem;$i += 2) {
521
522 #pair is valid if size is nonzero
523 if (hex($mem[$i+1]) != 0) {
524 $val .= "$mem[$i] $mem[$i+1] ";
525 }
526 }
527
528 $val =~ s/\s$//;
Matt Spinler995f2a22016-09-30 13:07:31 -0500529 $val .= " >";
Matt Spinler7d381e12016-09-27 14:27:24 -0500530 $property{reg} = $val;
531
532 return %property;
533}
534
535
536#Returns a list of compatible fields for the BMC itself.
537sub getBMCCompatibles()
538{
539 my @compats;
540
Matt Spinler23d47c22016-10-04 12:31:21 -0500541 #1st entry: <system mfgr>,<system name>-bmc
542 #2nd entry: <bmc mfgr>,<bmc model>
Matt Spinler7d381e12016-09-27 14:27:24 -0500543
Matt Spinler23d47c22016-10-04 12:31:21 -0500544 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() }) {
545 if ($g_targetObj->getType($target) eq "SYS") {
546 my $mfgr = $g_targetObj->getAttribute($target, "MANUFACTURER");
547 push @compats, lc "$mfgr,$g_systemName-bmc";
548 last;
549 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500550 }
551
552 push @compats, lc($g_bmcMfgr).",".lc($g_bmcModel);
553
554 return @compats;
555}
556
557
558#Returns a string for the system's BMC model property
559sub getSystemBMCModel()
560{
Matt Spinler995f2a22016-09-30 13:07:31 -0500561 #'<System> BMC'
Matt Spinler7d381e12016-09-27 14:27:24 -0500562 my $sys = lc $g_systemName;
563 $sys = uc(substr($sys, 0, 1)) . substr($sys, 1);
564
565 return $sys . " BMC";
566}
567
568
569#Prints a list of nodes at the same indent level
570# $f = file handle
571# $level = indent level (0,1,etc)
572# @nodes = array of node hashes to print, where the
573# key for the hash is the name of the node
574sub printNodes()
575{
576 my ($f, $level, @nodes) = @_;
577
578 foreach my $n (@nodes) {
579 my %node = %$n;
580
581 foreach my $name (sort keys %node) {
582 my %n = %{ $node{$name} };
583 printNode($f, $level, $name, %n);
584 }
585 }
586}
587
588
589#Print a single node and its children
590# $f = file handle
591# $level = indent level (0,1,etc)
592# $name = the name of the node - shows up as:
593# name { ...
594# %vals = The contents of the node, with the following options:
595# if the key is:
596# - 'DTSI_INCLUDE', then value gets turned into a #include
Matt Spinler995f2a22016-09-30 13:07:31 -0500597# - 'COMMENT', then value gets turned into a // comment
Matt Spinler7d381e12016-09-27 14:27:24 -0500598# - 'STANDALONE_PROPERTY' then value gets turned into: value;
599#
600# If the value is:
601# - a hash - then that hash gets turned into a child node
602# where the key is the name of the child node
Matt Spinler995f2a22016-09-30 13:07:31 -0500603# - an array of hashes indicates an array of child nodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500604sub printNode()
605{
606 my ($f, $level, $name, %vals) = @_;
607 my $include = "";
608
609 if ($level == 0) {
610 $name = "&".$name;
611 }
612
Matt Spinler995f2a22016-09-30 13:07:31 -0500613 print $f "\n";
614
615 if (exists $vals{COMMENT}) {
616 my @lines = split('\n', $vals{COMMENT});
617 foreach my $l (@lines) {
618 print $f indent($level) . "// $l\n";
619 }
620 }
621
622 print $f indent($level) . "$name {\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500623
624 foreach my $v (sort keys %vals) {
625
Matt Spinler995f2a22016-09-30 13:07:31 -0500626 next if ($v eq "COMMENT");
627
Matt Spinler7d381e12016-09-27 14:27:24 -0500628 #A header file include, print it later
629 if ($v eq DTSI_INCLUDE) {
630 $include = $vals{$v};
631 }
632 #A nested node
633 elsif (ref($vals{$v}) eq "HASH") {
634 printNode($f, $level+1, $v, %{$vals{$v}});
635 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500636 #An array of nested nodes
637 elsif (ref($vals{$v}) eq "ARRAY") {
638 my @array = @{$vals{$v}};
639 &printNodes($f, $level+1, @array);
640 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500641 elsif ($vals{$v} ne STANDALONE_PROPERTY) {
642 printProperty($f, $level+1, $v, $vals{$v});
643 }
644 else {
645 printStandaloneProperty($f, $level+1, $v);
646 }
647 }
648
649 #Now print the includes, if any.
650 if ($include ne "") {
651 my @incs = split(',', $include);
652 foreach my $i (@incs) {
653 print $f "#include \"$i\";\n";
654 }
655 }
656
657 print $f indent($level) . "};\n";
658}
659
660
661#Prints a comma separated list of properties.
662#e.g. a = "b, c, d";
663# $f = file handle
664# $level = indent level (0,1,etc)
665# $name = name of property
666# @vals = list of property values
667sub printPropertyList()
668{
669 my ($f, $level, $name, @vals) = @_;
670
671 print $f indent($level) . "$name = ";
672
673 for (my $i = 0;$i < scalar @vals; $i++) {
674 print $f "\"$vals[$i]\"";
675 if ($i < (scalar(@vals) - 1)) {
676 print $f ", ";
677 }
678 }
679 print $f ";\n"
680}
681
682
683#Prints a single property. e.g. a = "b";
684# $f = file handle
685# $level = indent level (0,1,etc)
686# $name = name of property
687# @vals = property values
688sub printProperty()
689{
690 my ($f, $level, $name, $val) = @_;
Matt Spinler23d47c22016-10-04 12:31:21 -0500691 my $quote = "\"";
692
693 $val = convertAlias($val);
694
695 #properties with < > or single word aliases don't need quotes
696 if (($val =~ /<.*>/) || ($val =~ /^&\w+$/)) {
697 $quote = "";
698 }
699
700 print $f indent($level) . "$name = $quote$val$quote;\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500701}
702
703
704#Prints a standalone property e.g. some-property;
705# $f = file handle
706# $level = indent level (0,1,etc)
707# $name = name of property
708sub printStandaloneProperty()
709{
710 my ($f, $level, $name) = @_;
711 print $f indent($level) . "$name;\n";
712}
713
714
715#Replace '(alias)' with '&'.
716#Needed because Serverwiz doesn't properly escape '&'s in the XML,
717#so the '(alias)' string is used to represent the alias
718#specifier instead of '&'.
719sub convertAlias() {
720 my $val = shift;
721 $val =~ s/\(alias\)/&/g;
722 return $val
723}
724
725
726#Returns the target for the BMC chip.
727#Not worrying about multiple BMC systems for now.
728sub getBMCTarget()
729{
730 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() })
731 {
732 if ($g_targetObj->getType($target) eq "BMC") {
733 return $target;
734 }
735 }
736 return "";
737}
738
739
740#Prints the device tree version line.
741# $f = file handle
742sub printVersion()
743{
744 my $f = shift;
745 print $f VERSION."\n"
746}
747
748
749#Prints the #include line for pulling in an include file.
750# $f = file handle
751# $level = indent level (0,1,etc)
752sub printIncludes()
753{
754 my ($f, $level) = @_;
755 my @includes = getIncludes($g_bmc);
756
757 foreach my $i (@includes) {
758 #if a .dtsi, gets " ", otherwise < >
759 if ($i =~ /\.dtsi$/) {
760 $i = "\"" . $i . "\"";
761 }
762 else {
763 $i = "<" . $i . ">";
764 }
765 print $f indent($level) . "#include $i;\n";
766 }
767}
768
769
770#Returns an array of includes from the BMC_DT_INCLUDES attribute
771#on the target passed in.
772# $target = the target to get the includes from
773sub getIncludes()
774{
775 my $target = shift;
776 my @includes;
777
Matt Spinler7d381e12016-09-27 14:27:24 -0500778 if (!$g_targetObj->isBadAttribute($target, "BMC_DT_INCLUDES")) {
779 my $attr = $g_targetObj->getAttribute($target, "BMC_DT_INCLUDES");
Matt Spinler995f2a22016-09-30 13:07:31 -0500780 $attr =~ s/\s+//g; #remove whitespace
781 $attr =~ s/NA,*//g; #remove the NAs
Matt Spinler7d381e12016-09-27 14:27:24 -0500782 my @incs = split(',', $attr);
783
784 foreach my $i (@incs) {
Matt Spinler995f2a22016-09-30 13:07:31 -0500785 push @includes, $i
Matt Spinler7d381e12016-09-27 14:27:24 -0500786 }
787 }
788
789 return @includes;
790}
791
792
793#Prints the root node starting bracket.
794# $f = file handle
795sub printRootNodeStart() {
796 my $f = shift;
797 print $f "\\ \{\n";
798}
799
800
801#Prints the root node ending bracket.
802# $f = file handle
803# $level = indent level (0,1,etc)
804sub printRootNodeEnd() {
805 my ($f, $level) = @_;
806 print $f indent($level)."\};\n";
807}
808
809
810#Returns a string that can be used to indent based on the
811#level passed in. Each level is an additional 4 spaces.
812# $level = indent level (0,1,etc)
813sub indent() {
814 my $level = shift;
815 return ' ' x ($level * 4);
816}
817
818
Matt Spinler995f2a22016-09-30 13:07:31 -0500819#Will look for all the connections of the specified type coming from
820#any sub target of the specified target, instead of just 1 level down
821#like the Targets inteface does. Needed because sometimes we have
822#target->pingroup->sourceunit instead of just target->sourceunit
823# $target = the target to find connections off of
824# $bus = the bus type
825# $partType = destination part type, leave off if a don't care
826sub findConnections() {
827 my ($target, $bus, $partType) = @_;
828 my %allConnections;
829 my $i = 0;
830
831 #get the ones from target->child
832 my $connections = $g_targetObj->findConnections($target, $bus, $partType);
833 if ($connections ne "") {
834 foreach my $c (@{$connections->{CONN}}) {
835 $allConnections{CONN}[$i] = { %{$c} };
836 $i++;
837 }
838 }
839
840 #get everything deeper
841 my @children = getAllTargetChildren($target);
842 foreach my $c (@children) {
843 my $connections = $g_targetObj->findConnections($c, $bus, $partType);
844 if ($connections ne "") {
845
846 foreach my $c (@{$connections->{CONN}}) {
847 $allConnections{CONN}[$i] = { %{$c} };
848 $i++;
849 }
850 }
851 }
852
Matt Spinler23d47c22016-10-04 12:31:21 -0500853 #Match the Targets::findConnections return strategy
854 if (!keys %allConnections) {
855 return "";
856 }
857
Matt Spinler995f2a22016-09-30 13:07:31 -0500858 return \%allConnections;
859}
860
861#Returns every sub target, not just the 1st level children.
862# $target = the target to find the children of
863sub getAllTargetChildren()
864{
865 my $target = shift;
866 my @children;
867
868 my $targets = $g_targetObj->getTargetChildren($target);
869 if ($targets ne "") {
870
871 foreach my $t (@$targets) {
872 push @children, $t;
873 my @more = getAllTargetChildren($t);
874 push @children, @more;
875 }
876 }
877
878 return @children;
879}
880
881
Matt Spinler7d381e12016-09-27 14:27:24 -0500882sub printUsage
883{
884 print "gen_devtree.pl -x [XML filename] -o [output filename]\n";
885 exit(1);
886}