blob: 39040ba2a517797df7f51c4e34ac9e189d81b755 [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 Spinler7d381e12016-09-27 14:27:24 -050049printNode($f, 1, "chosen", getChosen());
50printNode($f, 1, "memory", getMemory($g_bmc));
51
Matt Spinler995f2a22016-09-30 13:07:31 -050052printNodes($f, 1, getSpiFlashNodes());
53
54printNode($f, 1, "leds", getLEDNode());
55
Matt Spinler7d381e12016-09-27 14:27:24 -050056printRootNodeEnd($f, 0);
57
Matt Spinler995f2a22016-09-30 13:07:31 -050058#TODO: I2C, aliases, pinctlr
Matt Spinler7d381e12016-09-27 14:27:24 -050059printNodes($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
67
68#Return a hash that represents the 'chosen' node
Matt Spinler995f2a22016-09-30 13:07:31 -050069#Will look like:
70# chosen {
71# stdout-path = ...
72# bootargs = ...
73# }
Matt Spinler7d381e12016-09-27 14:27:24 -050074sub getChosen()
75{
76 my $bmcStdOut = $g_targetObj->getAttributeField($g_bmc, "BMC_DT_CHOSEN",
77 "stdout-path");
78 my $args = $g_targetObj->getAttributeField($g_bmc, "BMC_DT_CHOSEN",
79 "bootargs");
80 my %chosen;
81 $chosen{"stdout-path"} = $bmcStdOut;
82 $chosen{"bootargs"} = $args;
83 return %chosen;
84}
85
86
Matt Spinler995f2a22016-09-30 13:07:31 -050087#Gets the nodes that represents the BMC's SPI flash chips. They're based
88#on information from the spi-master-unit end of the connection, with
89#a subnode of information from the destination chip.
90#On ASPEED chips, they're nested under the ahb node (Advanced
91#High-performance Bus).
92#Will look like:
93# ahb {
94# fmc@... {
95# reg = ...
96# #address-cells = ...
97# #size-cells = ...
98# #compatible = ...
99#
100# flash@... {
101# reg = ...
102# compatible = ...
103# label = ...
104# #include ...
105# }
106# }
107# spi@... {
108# ...
109# }
110# }
111sub getSpiFlashNodes()
Matt Spinler7d381e12016-09-27 14:27:24 -0500112{
Matt Spinler995f2a22016-09-30 13:07:31 -0500113 my %parentNode, my %node, my @nodes;
114 my $lastParentNodeName = "default";
115 my $parentNodeName = "ahb";
Matt Spinler7d381e12016-09-27 14:27:24 -0500116
Matt Spinler995f2a22016-09-30 13:07:31 -0500117 my $connections = findConnections($g_bmc, "SPI", "FLASH");
118 if ($connections eq "") {
119 print "WARNING: No SPI flashes found connected to the BMC\n";
120 return @nodes;
121 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500122
Matt Spinler995f2a22016-09-30 13:07:31 -0500123 foreach my $spi (@{$connections->{CONN}}) {
Matt Spinler7d381e12016-09-27 14:27:24 -0500124
Matt Spinler995f2a22016-09-30 13:07:31 -0500125 my %unitNode; #Node for the SPI master unit
126 my %flashNode; #subnode for the flash chip itself
127 my $flashNodeName = "flash";
128 my $nodeLabel = "";
129 my @addresses;
Matt Spinler7d381e12016-09-27 14:27:24 -0500130
Matt Spinler7d381e12016-09-27 14:27:24 -0500131
Matt Spinler995f2a22016-09-30 13:07:31 -0500132 #Adds a comment into the output file about the MRW connection
133 #that makes up this node. Not that {SOURCE} always represents
134 #the master unit, and DEST_PARENT represents the destination
135 #chip. The destination unit {DEST} isn't usually that interesting.
136 $unitNode{COMMENT} = "$spi->{SOURCE} ->\n$spi->{DEST_PARENT}";
137
138 #These flashes are nested in the 'ahb' (an internal chip bus)
139 #node in ASPEED chips. Get the name of it here. Will default
140 #to 'ahb' if not set.
141 if (!$g_targetObj->isBadAttribute($spi->{SOURCE},
142 "INTERNAL_BUS", "NA")) {
143 $parentNodeName = $g_targetObj->getAttribute($spi->{SOURCE},
144 "INTERNAL_BUS");
145 #Not going to support this unless we have to
146 if ($parentNodeName != $lastParentNodeName) {
147 die "ERROR: SPI master unit $spi->{SOURCE} has a " .
148 "different internal bus name $parentNodeName than " .
149 "previous name $lastParentNodeName\n";
150 }
151 else {
152 $lastParentNodeName = $parentNodeName;
Matt Spinler7d381e12016-09-27 14:27:24 -0500153 }
154 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500155 else {
156 print "WARNING: No INTERNAL_BUS attribute value found for " .
157 "SPI flash unit $spi->{SOURCE}. Using '$parentNodeName'\n";
158 }
159
160 #The reg base and size of the unit will be added into
161 #the reg property
162 my $regBase = $g_targetObj->getAttribute($spi->{SOURCE},
163 "BMC_DT_REG_BASE");
164 my $regSize = $g_targetObj->getAttribute($spi->{SOURCE},
165 "BMC_DT_REG_SIZE");
166
167 #There is also another memory range that goes into reg
168 my %sourceRegHash = getMemory($spi->{SOURCE});
169
170 #Insert the regBase and regSize to the memory < ... > property
171 $unitNode{reg} = "< $regBase $regSize " . substr($sourceRegHash{reg}, 2);
172
173 #usually, this will be something like 'smc' or 'spi'
174 my $nodeName = "spi";
175 if (!$g_targetObj->isBadAttribute($spi->{SOURCE},
176 "BMC_DT_NODE_NAME")) {
177 $nodeName = $g_targetObj->getAttribute($spi->{SOURCE},
178 "BMC_DT_NODE_NAME");
179 }
180 else {
181 print "WARNING: No BMC_DT_NODE_NAME attribute value found for " .
182 "SPI flash unit $spi->{SOURCE}. Using 'spi'\n";
183 }
184
185 #now turn it into something like fmc@...
186 $nodeName .= "@".$regBase;
187
188 if (!$g_targetObj->isBadAttribute($spi->{SOURCE},
189 "BMC_DT_COMPATIBLE")) {
190 $unitNode{compatible} = $g_targetObj->
191 getAttribute($spi->{SOURCE}, "BMC_DT_COMPATIBLE");
192 }
193 else {
194 print "WARNING: No BMC_DT_COMPATIBLE attribute found for SPI " .
195 "flash unit $spi->{SOURCE}\n";
196 }
197
198 #The flash chip has its one reg property as well
199 if (!$g_targetObj->isBadAttribute($spi->{DEST_PARENT},
200 "BMC_DT_REG_PROPERTY")) {
201 $flashNode{reg} = $g_targetObj->getAttribute($spi->{DEST_PARENT},
202 "BMC_DT_REG_PROPERTY");
203 $flashNode{reg} = "<" . $flashNode{reg} . ">";
204 }
205 else {
206 print "WARNING: No BMC_REG_PROPERTY attribute found for SPI " .
207 "flash $spi->{DEST_PARENT}. Using <0>.\n";
208 $flashNode{reg} = "<0>";
209 }
210
211 if (!$g_targetObj->isBadAttribute($spi->{DEST_PARENT},
212 "BMC_DT_COMPATIBLE")) {
213 $flashNode{compatible} = $g_targetObj->
214 getAttribute($spi->{DEST_PARENT}, "BMC_DT_COMPATIBLE");
215 }
216 else {
217 print "WARNING: No BMC_DT_COMPATIBLE attribute found for SPI " .
218 "flash $spi->{DEST_PARENT}\n";
219 }
220
221 if (!$g_targetObj->isBadAttribute($spi->{DEST_PARENT},
222 "BMC_DT_LABEL_PROPERTY")) {
223 $flashNode{label} = $g_targetObj->
224 getAttribute($spi->{DEST_PARENT}, "BMC_DT_LABEL_PROPERTY");
225 }
226
227 #Some flash chips have a .dtsi include to pull in more properties.
228 #Future - contents of the includes could be pulled into the MRW
229 #as new attributes.
230 if (!$g_targetObj->isBadAttribute($spi->{DEST_PARENT},
231 "BMC_DT_INCLUDES")) {
232 my $incs = $g_targetObj->
233 getAttribute($spi->{DEST_PARENT}, "BMC_DT_INCLUDES");
234 #first remove the spaces and NAs
235 $incs =~ s/\s+//g;
236 $incs =~ s/NA,*//g;
237 $flashNode{DTSI_INCLUDE} = $incs;
238 }
239
240 #the flash subnode name also has its reg[0] appended
241 #like flash@...
242 @addresses = split(' ', $flashNode{reg});
243 $addresses[0] =~ s/<//;
244 $addresses[0] =~ s/>//;
245 $flashNodeName .= "@" . $addresses[0];
246 $unitNode{$flashNodeName} = { %flashNode };
247
248 #For now, just support a chip with 1 reg value
249 if (scalar @addresses == 1) {
250 $unitNode{'#address-cells'} = "<1>";
251 $unitNode{'#size-cells'} = "<0>";
252 }
253 else {
254 die "ERROR: Unsupported number of <reg> entries " .
255 "in flash node $flashNodeName for SPI flash " .
256 "$spi->{DEST_PARENT}. Only 1 entry supported.\n";
257 }
258
259 #This node will end up being in an array on the parent node
260 my %node;
261 $node{$nodeName} = { %unitNode };
262 push @nodes, { %node };
Matt Spinler7d381e12016-09-27 14:27:24 -0500263 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500264
265 $parentNode{$parentNodeName}{nodes} = [ @nodes ];
266
267 #There is always just one in the array
268 my @finalNodes;
269 push @finalNodes, { %parentNode };
270 return @finalNodes;
271}
272
273
274#Returns a hash that represents the leds node by finding all of the
275#GPIO connections to LEDs.
276#Node will look like:
277# leds {
278# <ledname> {
279# gpios = &gpio ASPEED_GPIO(x, y) GPIO_ACTIVE_xxx>
280# };
281# <another ledname> {
282# ...
283# }
284sub getLEDNode()
285{
286 my %leds;
287
288 $leds{compatible} = "gpio-led";
289
290 my $connections = findConnections($g_bmc, "GPIO", "LED");
291
292 if ($connections eq "") {
293 print "WARNING: No LEDs found connected to the BMC\n";
294 return %leds;
295 }
296
297 foreach my $gpio (@{$connections->{CONN}}) {
298 my %ledNode;
299
300 $ledNode{COMMENT} = "$gpio->{SOURCE} ->\n$gpio->{DEST_PARENT}";
301
302 #The node name will be the simplified LED name
303 my $name = $gpio->{DEST_PARENT};
304 $name =~ s/(-\d+$)//; #remove trailing position
305 $name =~ s/.*\///; #remove the front of the path
306
307 #For now only supports ASPEED.
308 if (uc($g_bmcMfgr) ne "ASPEED") {
309 die "ERROR: Unsupported BMC manufacturer $g_bmcMfgr\n";
310 }
311 my $num = $g_targetObj->getAttribute($gpio->{SOURCE}, "PIN_NUM");
312 my $macro = getAspeedGpioMacro($num);
313
314 #If it's active high or low
315 my $state = $g_targetObj->getAttribute($gpio->{DEST_PARENT}, "ON_STATE");
316 my $activeString = getGpioActiveString($state);
317
318 $ledNode{gpios} = "<&gpio $macro $activeString>";
319
320 $leds{$name} = { %ledNode };
321 }
322
323 return %leds;
324}
325
326
327#Returns a either GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW
328# $val = either a 1 or a 0 for active high or low
329sub getGpioActiveString() {
330 my $val = shift;
331
332 if ($val == 0) {
333 return "GPIO_ACTIVE_LOW";
334 }
335
336 return "GPIO_ACTIVE_HIGH";
337}
338
339
340#Turns a GPIO number into something like ASPEED_GPIO(A, 0) for the
341#ASPEED GPIO numbering scheme A[0-7] -> Z[0-7] and then starts at
342#AA[0-7] after that.
343# $num = the GPIO number
344sub getAspeedGpioMacro() {
345 my $num = shift;
346 my $char;
347 my $offset = $num % 8;
348 my $block = int($num / 8);
349
350 #If past Z, wraps to AA, AB, etc
351 if ((ord('A') + $block) > ord('Z')) {
352 #how far past Z?
353 $char = $block - (ord('Z') - ord('A'));
354
355 #Don't let it wrap twice
356 if ($char > (ord('Z') - ord('A') + 1)) {
357 die "ERROR: Invalid PIN_NUM value $num found for GPIO\n";
358 }
359
360 #start back at 'A' again, and convert to a character
361 $char = chr($char + ord('A') - 1);
362
363 #Add in a bonus 'A', to get something like AB
364 $char = "A".$char;
365 }
366 else {
367 $char = ord('A') + $block;
368 $char = chr($char);
369 }
370
371 return "ASPEED_GPIO($char, $offset)";
372}
373
374
375#Returns a list of hashes that represent the UART nodes on the BMC by
376#finding the UART connections.
377#Nodes will look like:
378# &uartX {
379# status = "okay"
380# }
381sub getUARTNodes()
382{
383 my @nodes;
384
385 my $connections = findConnections($g_bmc, "UART");
386
387 if ($connections eq "") {
388 print "WARNING: No UART buses found connected to the BMC\n";
389 return @nodes;
390 }
391
392 foreach my $uart (@{$connections->{CONN}}) {
393 my %node;
394
395 my $num = $g_targetObj->getAttribute($uart->{SOURCE}, "CHIP_UNIT");
396 my $name = "uart$num";
397
398 $node{$name}{status} = "okay";
399 $node{$name}{COMMENT} = "$uart->{SOURCE} ->\n$uart->{DEST_PARENT}";
400
401 push @nodes, { %node };
402 }
403
Matt Spinler7d381e12016-09-27 14:27:24 -0500404 return @nodes;
405}
406
407
Matt Spinler995f2a22016-09-30 13:07:31 -0500408#Returns a list of hashes that represent the MAC (ethernet) nodes on the BMC
409#by finding the connections of type ETHERNET.
410#Nodes will look like:
411# &macX {
412# ...
413# }
414sub getMacNodes()
415{
416 my @nodes;
417
418 my $connections = findConnections($g_bmc, "ETHERNET");
419
420 if ($connections eq "") {
421 print "WARNING: No ethernet buses found connected to the BMC\n";
422 return @nodes;
423 }
424
425 foreach my $eth (@{$connections->{CONN}}) {
426 my %node;
427
428 my $num = $g_targetObj->getAttribute($eth->{SOURCE}, "CHIP_UNIT");
429 my $ncsi = $g_targetObj->getAttribute($eth->{SOURCE}, "NCSI_MODE");
430 my $hwChecksum = $g_targetObj->getAttribute($eth->{SOURCE},
431 "USE_HW_CHECKSUM");
432
433 my $name = "mac$num";
434 $node{$name}{status} = "okay";
435
436 if ($ncsi == 1) {
437 $node{$name}{"use-ncsi"} = STANDALONE_PROPERTY;
438 }
439 if ($hwChecksum == 0) {
440 $node{$name}{"no-hw-checksum"} = STANDALONE_PROPERTY;
441 }
442
443 $node{$name}{COMMENT} = "$eth->{SOURCE} ->\n$eth->{DEST_PARENT}";
444
445 push @nodes, { %node };
446 }
447
448 return @nodes;
449}
450
451
452#Returns a list of hashes that represent the virtual UART nodes
453#Node will look like:
454# &vuart {
455# status = "okay"
456# }
Matt Spinler7d381e12016-09-27 14:27:24 -0500457sub getVuartNodes()
458{
459 my @nodes;
460 my %node;
461
462 #For now, enable 1 node all the time.
Matt Spinler995f2a22016-09-30 13:07:31 -0500463 #TBD if this needs to be fixed
Matt Spinler7d381e12016-09-27 14:27:24 -0500464 $node{vuart}{status} = "okay";
465
466 push @nodes, { %node };
467
468 return @nodes;
469}
470
471
472#Returns a hash{'reg'} = "<.....>" based on the
473#BMC_DT_MEMORY attribute. This is used to display
474#memory ranges.
475sub getMemory()
476{
477 my $target = shift;
478 my $memory = $g_targetObj->getAttribute($target, "BMC_DT_MEMORY");
479 my @mem = split(',', $memory);
480 my %property;
Matt Spinler995f2a22016-09-30 13:07:31 -0500481 my $val = "< ";
Matt Spinler7d381e12016-09-27 14:27:24 -0500482
483 #Encoded as 4 <base address>,<size> pairs of memory ranges
484 #Unused ranges are all 0s.
485 #For now, assumes 32 bit numbers, revisit later for 64 bit support
486 #Convert it into: <num1 num2 num3 num4 etc>
487
488 for (my $i = 0;$i < scalar @mem;$i += 2) {
489
490 #pair is valid if size is nonzero
491 if (hex($mem[$i+1]) != 0) {
492 $val .= "$mem[$i] $mem[$i+1] ";
493 }
494 }
495
496 $val =~ s/\s$//;
Matt Spinler995f2a22016-09-30 13:07:31 -0500497 $val .= " >";
Matt Spinler7d381e12016-09-27 14:27:24 -0500498 $property{reg} = $val;
499
500 return %property;
501}
502
503
504#Returns a list of compatible fields for the BMC itself.
505sub getBMCCompatibles()
506{
507 my @compats;
508
509 #The first one is from the MRW, the next one is more generic
510 #and just <mfgr>-<model>.
511
512 if (!$g_targetObj->isBadAttribute($g_bmc, "BMC_DT_COMPATIBLE", "NA")) {
513 my $attr = $g_targetObj->getAttribute($g_bmc, "BMC_DT_COMPATIBLE");
514 push @compats, $attr;
515 }
516
517 push @compats, lc($g_bmcMfgr).",".lc($g_bmcModel);
518
519 return @compats;
520}
521
522
523#Returns a string for the system's BMC model property
524sub getSystemBMCModel()
525{
Matt Spinler995f2a22016-09-30 13:07:31 -0500526 #'<System> BMC'
Matt Spinler7d381e12016-09-27 14:27:24 -0500527 my $sys = lc $g_systemName;
528 $sys = uc(substr($sys, 0, 1)) . substr($sys, 1);
529
530 return $sys . " BMC";
531}
532
533
534#Prints a list of nodes at the same indent level
535# $f = file handle
536# $level = indent level (0,1,etc)
537# @nodes = array of node hashes to print, where the
538# key for the hash is the name of the node
539sub printNodes()
540{
541 my ($f, $level, @nodes) = @_;
542
543 foreach my $n (@nodes) {
544 my %node = %$n;
545
546 foreach my $name (sort keys %node) {
547 my %n = %{ $node{$name} };
548 printNode($f, $level, $name, %n);
549 }
550 }
551}
552
553
554#Print a single node and its children
555# $f = file handle
556# $level = indent level (0,1,etc)
557# $name = the name of the node - shows up as:
558# name { ...
559# %vals = The contents of the node, with the following options:
560# if the key is:
561# - 'DTSI_INCLUDE', then value gets turned into a #include
Matt Spinler995f2a22016-09-30 13:07:31 -0500562# - 'COMMENT', then value gets turned into a // comment
Matt Spinler7d381e12016-09-27 14:27:24 -0500563# - 'STANDALONE_PROPERTY' then value gets turned into: value;
564#
565# If the value is:
566# - a hash - then that hash gets turned into a child node
567# where the key is the name of the child node
Matt Spinler995f2a22016-09-30 13:07:31 -0500568# - an array of hashes indicates an array of child nodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500569sub printNode()
570{
571 my ($f, $level, $name, %vals) = @_;
572 my $include = "";
573
574 if ($level == 0) {
575 $name = "&".$name;
576 }
577
Matt Spinler995f2a22016-09-30 13:07:31 -0500578 print $f "\n";
579
580 if (exists $vals{COMMENT}) {
581 my @lines = split('\n', $vals{COMMENT});
582 foreach my $l (@lines) {
583 print $f indent($level) . "// $l\n";
584 }
585 }
586
587 print $f indent($level) . "$name {\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500588
589 foreach my $v (sort keys %vals) {
590
Matt Spinler995f2a22016-09-30 13:07:31 -0500591 next if ($v eq "COMMENT");
592
Matt Spinler7d381e12016-09-27 14:27:24 -0500593 #A header file include, print it later
594 if ($v eq DTSI_INCLUDE) {
595 $include = $vals{$v};
596 }
597 #A nested node
598 elsif (ref($vals{$v}) eq "HASH") {
599 printNode($f, $level+1, $v, %{$vals{$v}});
600 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500601 #An array of nested nodes
602 elsif (ref($vals{$v}) eq "ARRAY") {
603 my @array = @{$vals{$v}};
604 &printNodes($f, $level+1, @array);
605 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500606 elsif ($vals{$v} ne STANDALONE_PROPERTY) {
607 printProperty($f, $level+1, $v, $vals{$v});
608 }
609 else {
610 printStandaloneProperty($f, $level+1, $v);
611 }
612 }
613
614 #Now print the includes, if any.
615 if ($include ne "") {
616 my @incs = split(',', $include);
617 foreach my $i (@incs) {
618 print $f "#include \"$i\";\n";
619 }
620 }
621
622 print $f indent($level) . "};\n";
623}
624
625
626#Prints a comma separated list of properties.
627#e.g. a = "b, c, d";
628# $f = file handle
629# $level = indent level (0,1,etc)
630# $name = name of property
631# @vals = list of property values
632sub printPropertyList()
633{
634 my ($f, $level, $name, @vals) = @_;
635
636 print $f indent($level) . "$name = ";
637
638 for (my $i = 0;$i < scalar @vals; $i++) {
639 print $f "\"$vals[$i]\"";
640 if ($i < (scalar(@vals) - 1)) {
641 print $f ", ";
642 }
643 }
644 print $f ";\n"
645}
646
647
648#Prints a single property. e.g. a = "b";
649# $f = file handle
650# $level = indent level (0,1,etc)
651# $name = name of property
652# @vals = property values
653sub printProperty()
654{
655 my ($f, $level, $name, $val) = @_;
656 print $f indent($level) . "$name = \"" . convertAlias($val) . "\";\n";
657}
658
659
660#Prints a standalone property e.g. some-property;
661# $f = file handle
662# $level = indent level (0,1,etc)
663# $name = name of property
664sub printStandaloneProperty()
665{
666 my ($f, $level, $name) = @_;
667 print $f indent($level) . "$name;\n";
668}
669
670
671#Replace '(alias)' with '&'.
672#Needed because Serverwiz doesn't properly escape '&'s in the XML,
673#so the '(alias)' string is used to represent the alias
674#specifier instead of '&'.
675sub convertAlias() {
676 my $val = shift;
677 $val =~ s/\(alias\)/&/g;
678 return $val
679}
680
681
682#Returns the target for the BMC chip.
683#Not worrying about multiple BMC systems for now.
684sub getBMCTarget()
685{
686 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() })
687 {
688 if ($g_targetObj->getType($target) eq "BMC") {
689 return $target;
690 }
691 }
692 return "";
693}
694
695
696#Prints the device tree version line.
697# $f = file handle
698sub printVersion()
699{
700 my $f = shift;
701 print $f VERSION."\n"
702}
703
704
705#Prints the #include line for pulling in an include file.
706# $f = file handle
707# $level = indent level (0,1,etc)
708sub printIncludes()
709{
710 my ($f, $level) = @_;
711 my @includes = getIncludes($g_bmc);
712
713 foreach my $i (@includes) {
714 #if a .dtsi, gets " ", otherwise < >
715 if ($i =~ /\.dtsi$/) {
716 $i = "\"" . $i . "\"";
717 }
718 else {
719 $i = "<" . $i . ">";
720 }
721 print $f indent($level) . "#include $i;\n";
722 }
723}
724
725
726#Returns an array of includes from the BMC_DT_INCLUDES attribute
727#on the target passed in.
728# $target = the target to get the includes from
729sub getIncludes()
730{
731 my $target = shift;
732 my @includes;
733
Matt Spinler7d381e12016-09-27 14:27:24 -0500734 if (!$g_targetObj->isBadAttribute($target, "BMC_DT_INCLUDES")) {
735 my $attr = $g_targetObj->getAttribute($target, "BMC_DT_INCLUDES");
Matt Spinler995f2a22016-09-30 13:07:31 -0500736 $attr =~ s/\s+//g; #remove whitespace
737 $attr =~ s/NA,*//g; #remove the NAs
Matt Spinler7d381e12016-09-27 14:27:24 -0500738 my @incs = split(',', $attr);
739
740 foreach my $i (@incs) {
Matt Spinler995f2a22016-09-30 13:07:31 -0500741 push @includes, $i
Matt Spinler7d381e12016-09-27 14:27:24 -0500742 }
743 }
744
745 return @includes;
746}
747
748
749#Prints the root node starting bracket.
750# $f = file handle
751sub printRootNodeStart() {
752 my $f = shift;
753 print $f "\\ \{\n";
754}
755
756
757#Prints the root node ending bracket.
758# $f = file handle
759# $level = indent level (0,1,etc)
760sub printRootNodeEnd() {
761 my ($f, $level) = @_;
762 print $f indent($level)."\};\n";
763}
764
765
766#Returns a string that can be used to indent based on the
767#level passed in. Each level is an additional 4 spaces.
768# $level = indent level (0,1,etc)
769sub indent() {
770 my $level = shift;
771 return ' ' x ($level * 4);
772}
773
774
Matt Spinler995f2a22016-09-30 13:07:31 -0500775#Will look for all the connections of the specified type coming from
776#any sub target of the specified target, instead of just 1 level down
777#like the Targets inteface does. Needed because sometimes we have
778#target->pingroup->sourceunit instead of just target->sourceunit
779# $target = the target to find connections off of
780# $bus = the bus type
781# $partType = destination part type, leave off if a don't care
782sub findConnections() {
783 my ($target, $bus, $partType) = @_;
784 my %allConnections;
785 my $i = 0;
786
787 #get the ones from target->child
788 my $connections = $g_targetObj->findConnections($target, $bus, $partType);
789 if ($connections ne "") {
790 foreach my $c (@{$connections->{CONN}}) {
791 $allConnections{CONN}[$i] = { %{$c} };
792 $i++;
793 }
794 }
795
796 #get everything deeper
797 my @children = getAllTargetChildren($target);
798 foreach my $c (@children) {
799 my $connections = $g_targetObj->findConnections($c, $bus, $partType);
800 if ($connections ne "") {
801
802 foreach my $c (@{$connections->{CONN}}) {
803 $allConnections{CONN}[$i] = { %{$c} };
804 $i++;
805 }
806 }
807 }
808
809 return \%allConnections;
810}
811
812#Returns every sub target, not just the 1st level children.
813# $target = the target to find the children of
814sub getAllTargetChildren()
815{
816 my $target = shift;
817 my @children;
818
819 my $targets = $g_targetObj->getTargetChildren($target);
820 if ($targets ne "") {
821
822 foreach my $t (@$targets) {
823 push @children, $t;
824 my @more = getAllTargetChildren($t);
825 push @children, @more;
826 }
827 }
828
829 return @children;
830}
831
832
Matt Spinler7d381e12016-09-27 14:27:24 -0500833sub printUsage
834{
835 print "gen_devtree.pl -x [XML filename] -o [output filename]\n";
836 exit(1);
837}