blob: 4b1b94f6042c8be95f43b98e3e23c3ca5d8c0aeb [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;
Matt Spinler30b461c2016-10-10 16:50:07 -050010use YAML::Tiny qw(LoadFile);
11use Scalar::Util qw(looks_like_number);
Matt Spinler7d381e12016-09-27 14:27:24 -050012
Matt Spinler30b461c2016-10-10 16:50:07 -050013use constant {
14 VERSION => "/dts-v1/;",
15 ZERO_LENGTH_PROPERTY => "zero_length_property",
16 PRE_ROOT_INCLUDES => "pre-root-node",
17 ROOT_INCLUDES => "root-node",
18 POST_ROOT_INCLUDES => "post-root-node"
19};
Matt Spinler74909132016-10-07 13:52:19 -050020
Matt Spinler7d381e12016-09-27 14:27:24 -050021
22my $serverwizFile;
Matt Spinler30b461c2016-10-10 16:50:07 -050023my $configFile;
Matt Spinler7d381e12016-09-27 14:27:24 -050024my $outputFile;
25my $debug;
26
27GetOptions("x=s" => \$serverwizFile,
Matt Spinler30b461c2016-10-10 16:50:07 -050028 "y=s" => \$configFile,
Matt Spinler7d381e12016-09-27 14:27:24 -050029 "o=s" => \$outputFile,
30 "d" => \$debug)
31or printUsage();
32
Matt Spinler30b461c2016-10-10 16:50:07 -050033if ((not defined $serverwizFile) || (not defined $outputFile) ||
34 (not defined $configFile)) {
Matt Spinler7d381e12016-09-27 14:27:24 -050035 printUsage();
36}
37
Matt Spinler30b461c2016-10-10 16:50:07 -050038my %g_configuration = %{ LoadFile($configFile) };
39
Matt Spinler7d381e12016-09-27 14:27:24 -050040my $g_targetObj = Targets->new;
41$g_targetObj->loadXML($serverwizFile);
42
Matt Spinler74909132016-10-07 13:52:19 -050043my ($g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName);
44setGlobalAttributes();
Matt Spinler7d381e12016-09-27 14:27:24 -050045
Matt Spinler30b461c2016-10-10 16:50:07 -050046my $g_i2cBusAdjust = 0;
47getI2CBusAdjust();
Matt Spinler7d381e12016-09-27 14:27:24 -050048
49open (my $f, ">$outputFile") or die "Could not open $outputFile\n";
50
51printVersion($f);
Matt Spinler30b461c2016-10-10 16:50:07 -050052printIncludes($f, PRE_ROOT_INCLUDES);
Matt Spinler7d381e12016-09-27 14:27:24 -050053printRootNodeStart($f);
54
55printPropertyList($f, 1, "model", getSystemBMCModel());
Matt Spinler7d381e12016-09-27 14:27:24 -050056printPropertyList($f, 1, "compatible", getBMCCompatibles());
Matt Spinler995f2a22016-09-30 13:07:31 -050057
Matt Spinler23d47c22016-10-04 12:31:21 -050058printNode($f, 1, "aliases", getAliases());
Matt Spinler7d381e12016-09-27 14:27:24 -050059printNode($f, 1, "chosen", getChosen());
Matt Spinler30b461c2016-10-10 16:50:07 -050060printNode($f, 1, "memory", getBmcMemory());
Matt Spinler7d381e12016-09-27 14:27:24 -050061
Matt Spinler995f2a22016-09-30 13:07:31 -050062printNodes($f, 1, getSpiFlashNodes());
63
64printNode($f, 1, "leds", getLEDNode());
65
Matt Spinler30b461c2016-10-10 16:50:07 -050066printIncludes($f, ROOT_INCLUDES);
67
Matt Spinler7d381e12016-09-27 14:27:24 -050068printRootNodeEnd($f, 0);
69
Matt Spinler74909132016-10-07 13:52:19 -050070printNodes($f, 0, getI2CNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050071printNodes($f, 0, getMacNodes());
Matt Spinler995f2a22016-09-30 13:07:31 -050072printNodes($f, 0, getUARTNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050073printNodes($f, 0, getVuartNodes());
74
Matt Spinler30b461c2016-10-10 16:50:07 -050075printIncludes($f, POST_ROOT_INCLUDES);
76
Matt Spinler7d381e12016-09-27 14:27:24 -050077close $f;
78exit 0;
79
80
Matt Spinler74909132016-10-07 13:52:19 -050081#Finds the values for these globals:
82# $g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName
83sub setGlobalAttributes()
84{
85 $g_bmc = getBMCTarget();
86 if (length($g_bmc) == 0) {
87 die "Unable to find a BMC in this system\n";
88 }
89
90 if ($g_targetObj->isBadAttribute($g_bmc, "MODEL")) {
91 die "The MODEL attribute on $g_bmc is missing or empty.\n";
92 }
93 $g_bmcModel = $g_targetObj->getAttribute($g_bmc, "MODEL");
94
95 if ($g_targetObj->isBadAttribute($g_bmc, "MANUFACTURER")) {
96 die "The MANUFACTURER attribute on $g_bmc is missing or empty.\n";
97 }
98 $g_bmcMfgr = $g_targetObj->getAttribute($g_bmc, "MANUFACTURER");
99
100 $g_systemName = $g_targetObj->getSystemName();
101 if (length($g_systemName) == 0) {
102 die "The SYSTEM_NAME attribute is not set on the system target.\n";
103 }
104}
105
106
Matt Spinler23d47c22016-10-04 12:31:21 -0500107#Returns a hash that represents the 'aliases' node.
108#Will look like:
109# aliases {
110# name1 = &val1;
111# name2 = &val2;
112# ...
113# }
114sub getAliases()
115{
116 my %aliases;
Matt Spinler23d47c22016-10-04 12:31:21 -0500117
Matt Spinler30b461c2016-10-10 16:50:07 -0500118 #Get the info from the config file
Matt Spinler23d47c22016-10-04 12:31:21 -0500119
Matt Spinler30b461c2016-10-10 16:50:07 -0500120 if ((not exists $g_configuration{aliases}) ||
121 (keys %{$g_configuration{aliases}} == 0)) {
122 print "WARNING: Missing or empty 'aliases' section in config file.\n";
123 return %aliases;
124 }
125 %aliases = %{ $g_configuration{aliases} };
126
127 #add a & reference if one is missing
128 foreach my $a (keys %aliases) {
129 if (($aliases{$a} !~ /^&/) && ($aliases{$a} !~ /^\(ref\)/)) {
130 $aliases{$a} = "(ref)$aliases{$a}";
Matt Spinler23d47c22016-10-04 12:31:21 -0500131 }
132 }
133
134 return %aliases;
135}
136
Matt Spinler7d381e12016-09-27 14:27:24 -0500137
138#Return a hash that represents the 'chosen' node
Matt Spinler995f2a22016-09-30 13:07:31 -0500139#Will look like:
140# chosen {
141# stdout-path = ...
142# bootargs = ...
143# }
Matt Spinler7d381e12016-09-27 14:27:24 -0500144sub getChosen()
145{
Matt Spinler7d381e12016-09-27 14:27:24 -0500146 my %chosen;
Matt Spinler30b461c2016-10-10 16:50:07 -0500147 my @allowed = qw(bootargs stdin-path stdout-path);
148
149 #Get the info from the config file
150
151 if (not exists $g_configuration{chosen}) {
152 die "ERROR: Missing 'chosen' section in config file.\n";
153 }
154 %chosen = %{ $g_configuration{chosen} };
155
156 #Check for allowed entries. Empty is OK.
157 foreach my $key (keys %chosen) {
158 my $found = 0;
159 foreach my $good (@allowed) {
160 if ($key eq $good) {
161 $found = 1;
162 }
163 }
164
165 if ($found == 0) {
166 die "Invalid entry $key in 'chosen' section in config file\n";
167 }
168 }
169
Matt Spinler7d381e12016-09-27 14:27:24 -0500170 return %chosen;
171}
172
173
Matt Spinler30b461c2016-10-10 16:50:07 -0500174#Return a hash that represents the 'memory' node.
Matt Spinler995f2a22016-09-30 13:07:31 -0500175#Will look like:
Matt Spinler30b461c2016-10-10 16:50:07 -0500176# memory {
177# reg = < base size >
178# }
179sub getBmcMemory()
180{
181 my %memory;
182
183 #Get the info from the config file
184
185 if (not exists $g_configuration{memory}) {
186 die "ERROR: Missing 'memory' section in config file.\n";
187 }
188
189 if ((not exists $g_configuration{memory}{base}) ||
190 ($g_configuration{memory}{base} !~ /0x/)) {
191 die "ERROR: The base entry in the memory section in the config " .
192 "file is either missing or invalid.\n";
193 }
194
195 if ((not exists $g_configuration{memory}{size}) ||
196 ($g_configuration{memory}{size} !~ /0x/)) {
197 die "ERROR: The size entry in the memory section in the config " .
198 "file is either missing or invalid.\n";
199 }
200
201 #Future: could do more validation on the actual values
202
203 $memory{reg} = "<$g_configuration{memory}{base} " .
204 "$g_configuration{memory}{size}>";
205
206 return %memory;
207}
208
209
210
Matt Spinler995f2a22016-09-30 13:07:31 -0500211sub getSpiFlashNodes()
Matt Spinler7d381e12016-09-27 14:27:24 -0500212{
Matt Spinler30b461c2016-10-10 16:50:07 -0500213 #TODO: A new binding is coming soon that is much more simple than
214 #the previous one. When that is available, this function will
215 #be updated to support it. Before then, a root node include
216 #will pick up the legacy spi flash nodes.
Matt Spinler995f2a22016-09-30 13:07:31 -0500217}
218
219
220#Returns a hash that represents the leds node by finding all of the
221#GPIO connections to LEDs.
222#Node will look like:
223# leds {
224# <ledname> {
225# gpios = &gpio ASPEED_GPIO(x, y) GPIO_ACTIVE_xxx>
226# };
227# <another ledname> {
228# ...
229# }
230sub getLEDNode()
231{
232 my %leds;
233
234 $leds{compatible} = "gpio-led";
235
236 my $connections = findConnections($g_bmc, "GPIO", "LED");
237
238 if ($connections eq "") {
239 print "WARNING: No LEDs found connected to the BMC\n";
240 return %leds;
241 }
242
243 foreach my $gpio (@{$connections->{CONN}}) {
244 my %ledNode;
245
246 $ledNode{COMMENT} = "$gpio->{SOURCE} ->\n$gpio->{DEST_PARENT}";
247
248 #The node name will be the simplified LED name
249 my $name = $gpio->{DEST_PARENT};
250 $name =~ s/(-\d+$)//; #remove trailing position
251 $name =~ s/.*\///; #remove the front of the path
252
253 #For now only supports ASPEED.
254 if (uc($g_bmcMfgr) ne "ASPEED") {
255 die "ERROR: Unsupported BMC manufacturer $g_bmcMfgr\n";
256 }
257 my $num = $g_targetObj->getAttribute($gpio->{SOURCE}, "PIN_NUM");
258 my $macro = getAspeedGpioMacro($num);
259
260 #If it's active high or low
261 my $state = $g_targetObj->getAttribute($gpio->{DEST_PARENT}, "ON_STATE");
262 my $activeString = getGpioActiveString($state);
263
264 $ledNode{gpios} = "<&gpio $macro $activeString>";
265
266 $leds{$name} = { %ledNode };
267 }
268
269 return %leds;
270}
271
272
273#Returns a either GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW
274# $val = either a 1 or a 0 for active high or low
275sub getGpioActiveString() {
276 my $val = shift;
277
278 if ($val == 0) {
279 return "GPIO_ACTIVE_LOW";
280 }
281
282 return "GPIO_ACTIVE_HIGH";
283}
284
285
286#Turns a GPIO number into something like ASPEED_GPIO(A, 0) for the
287#ASPEED GPIO numbering scheme A[0-7] -> Z[0-7] and then starts at
288#AA[0-7] after that.
289# $num = the GPIO number
290sub getAspeedGpioMacro() {
291 my $num = shift;
292 my $char;
293 my $offset = $num % 8;
294 my $block = int($num / 8);
295
296 #If past Z, wraps to AA, AB, etc
297 if ((ord('A') + $block) > ord('Z')) {
298 #how far past Z?
299 $char = $block - (ord('Z') - ord('A'));
300
301 #Don't let it wrap twice
302 if ($char > (ord('Z') - ord('A') + 1)) {
303 die "ERROR: Invalid PIN_NUM value $num found for GPIO\n";
304 }
305
306 #start back at 'A' again, and convert to a character
307 $char = chr($char + ord('A') - 1);
308
309 #Add in a bonus 'A', to get something like AB
310 $char = "A".$char;
311 }
312 else {
313 $char = ord('A') + $block;
314 $char = chr($char);
315 }
316
317 return "ASPEED_GPIO($char, $offset)";
318}
319
320
321#Returns a list of hashes that represent the UART nodes on the BMC by
322#finding the UART connections.
323#Nodes will look like:
324# &uartX {
325# status = "okay"
326# }
327sub getUARTNodes()
328{
329 my @nodes;
330
Matt Spinler23d47c22016-10-04 12:31:21 -0500331 #Using U750 for legacy MRW reasons
332 my $connections = findConnections($g_bmc, "U750");
Matt Spinler995f2a22016-09-30 13:07:31 -0500333
334 if ($connections eq "") {
335 print "WARNING: No UART buses found connected to the BMC\n";
336 return @nodes;
337 }
338
339 foreach my $uart (@{$connections->{CONN}}) {
340 my %node;
341
342 my $num = $g_targetObj->getAttribute($uart->{SOURCE}, "CHIP_UNIT");
343 my $name = "uart$num";
344
345 $node{$name}{status} = "okay";
346 $node{$name}{COMMENT} = "$uart->{SOURCE} ->\n$uart->{DEST_PARENT}";
347
348 push @nodes, { %node };
349 }
350
Matt Spinler7d381e12016-09-27 14:27:24 -0500351 return @nodes;
352}
353
354
Matt Spinler995f2a22016-09-30 13:07:31 -0500355#Returns a list of hashes that represent the MAC (ethernet) nodes on the BMC
356#by finding the connections of type ETHERNET.
357#Nodes will look like:
358# &macX {
359# ...
360# }
361sub getMacNodes()
362{
363 my @nodes;
364
365 my $connections = findConnections($g_bmc, "ETHERNET");
366
367 if ($connections eq "") {
368 print "WARNING: No ethernet buses found connected to the BMC\n";
369 return @nodes;
370 }
371
372 foreach my $eth (@{$connections->{CONN}}) {
373 my %node;
374
375 my $num = $g_targetObj->getAttribute($eth->{SOURCE}, "CHIP_UNIT");
376 my $ncsi = $g_targetObj->getAttribute($eth->{SOURCE}, "NCSI_MODE");
377 my $hwChecksum = $g_targetObj->getAttribute($eth->{SOURCE},
378 "USE_HW_CHECKSUM");
379
380 my $name = "mac$num";
381 $node{$name}{status} = "okay";
382
383 if ($ncsi == 1) {
Matt Spinler74909132016-10-07 13:52:19 -0500384 $node{$name}{"use-ncsi"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500385 }
386 if ($hwChecksum == 0) {
Matt Spinler74909132016-10-07 13:52:19 -0500387 $node{$name}{"no-hw-checksum"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500388 }
389
390 $node{$name}{COMMENT} = "$eth->{SOURCE} ->\n$eth->{DEST_PARENT}";
391
392 push @nodes, { %node };
393 }
394
395 return @nodes;
396}
397
398
399#Returns a list of hashes that represent the virtual UART nodes
400#Node will look like:
401# &vuart {
402# status = "okay"
403# }
Matt Spinler7d381e12016-09-27 14:27:24 -0500404sub getVuartNodes()
405{
406 my @nodes;
407 my %node;
408
409 #For now, enable 1 node all the time.
Matt Spinler995f2a22016-09-30 13:07:31 -0500410 #TBD if this needs to be fixed
Matt Spinler7d381e12016-09-27 14:27:24 -0500411 $node{vuart}{status} = "okay";
412
413 push @nodes, { %node };
414
415 return @nodes;
416}
417
Matt Spinler74909132016-10-07 13:52:19 -0500418#Returns a list of hashes that represent the I2C device nodes.
419#There is 1 parent node for each bus, which then have subnodes
420#for each device on that bus. If a bus doesn't have any
421#attached devices, it doesn't need to show up.
422#The nodes will look like:
423# &i2c0 {
424# status = "okay"
425# device1@addr { (addr = 7 bit I2C address)
426# reg = <addr>
427# compatible = ...
428# ...
429# }
430# device2@addr {
431# reg = <addr>
432# ...
433# }
434# }
435# &i2c1 {
436# ...
437# }
438sub getI2CNodes()
439{
440 my @nodes;
441 my %busNodes;
442
443 my $connections = findConnections($g_bmc, "I2C");
444
445 if ($connections eq "") {
446 print "WARNING: No I2C buses found connected to the BMC\n";
447 return @nodes;
448 }
449
450 foreach my $i2c (@{$connections->{CONN}}) {
451
452 my %deviceNode, my $deviceName;
453
454 $deviceNode{COMMENT} = "$i2c->{SOURCE} ->\n$i2c->{DEST_PARENT}";
455
456 $deviceName = lc $i2c->{DEST_PARENT};
457 $deviceName =~ s/-\d+$//; #remove trailing position
458 $deviceName =~ s/.*\///; #remove the front of the path
459
460 #Get the I2C address
461 my $i2cAddress = $g_targetObj->getAttribute($i2c->{DEST}, "I2C_ADDRESS");
462 $i2cAddress = hex($i2cAddress);
463 if ($i2cAddress == 0) {
464 die "ERROR: Missing I2C address on $i2c->{DEST}\n";
465 }
466
467 #Put it in the format we want to print it in
468 $i2cAddress = adjustI2CAddress($i2cAddress);
469 $deviceNode{reg} = "<$i2cAddress>";
470
471 $deviceName = makeNodeName($deviceName, $deviceNode{reg});
472
473 #Get the I2C bus number
474 if ($g_targetObj->isBadAttribute($i2c->{SOURCE},
475 "I2C_PORT")) {
476 die "ERROR: I2C_PORT attribute in $i2c->{DEST_PARENT} " .
477 "is either missing or empty.\n";
478 }
479
480 my $busNum = $g_targetObj->getAttribute($i2c->{SOURCE}, "I2C_PORT");
481 if ($busNum =~ /0x/i) {
482 $busNum = hex($busNum);
483 }
484
485 #Convert the number to the Linux numbering scheme.
Matt Spinler30b461c2016-10-10 16:50:07 -0500486 $busNum += $g_i2cBusAdjust;
Matt Spinler74909132016-10-07 13:52:19 -0500487
488 #Get the compatible property
489 if ($g_targetObj->isBadAttribute($i2c->{DEST_PARENT},
490 "BMC_DT_COMPATIBLE")) {
491 die "ERROR: BMC_DT_COMPATIBLE attribute in $i2c->{DEST_PARENT} " .
492 "is either missing or empty.\n";
493 }
494
495 $deviceNode{compatible} = $g_targetObj->getAttribute(
496 $i2c->{DEST_PARENT},
497 "BMC_DT_COMPATIBLE");
498
499 #Get any other part specific properties, where the property
500 #names are actually defined in the XML.
501 my %props = getPartDefinedDTProperties($i2c->{DEST_PARENT});
502 foreach my $prop (sort keys %props) {
503 $deviceNode{$prop} = $props{$prop};
504 }
505
506 #busNodeName is the hash twice so when we loop
507 #below it doesn't get lost
508 my $busNodeName = "i2c$busNum";
509 $busNodes{$busNodeName}{$busNodeName}{status} = "okay";
510 $busNodes{$busNodeName}{$busNodeName}{$deviceName} = { %deviceNode };
511 }
512
513 #Each bus gets its own hash entry in the array
514 for my $b (sort keys %busNodes) {
515 push @nodes, { %{$busNodes{$b}} };
516 }
517
518 return @nodes;
519}
520
521
522#Returns a hash of property names and values that should be stored in
523#the device tree node for this device. The names of the properties and
524#the attributes to find their values in are stored in the
525#BMC_DT_ATTR_NAMES attribute in the chip.
526# $chip = the chip target
527sub getPartDefinedDTProperties()
528{
529 my $chip = shift;
530 my %props;
531
532 if ($g_targetObj->isBadAttribute($chip, "BMC_DT_ATTR_NAMES")) {
533 return %props;
534 }
535
536 my $attr = $g_targetObj->getAttribute($chip, "BMC_DT_ATTR_NAMES");
537 $attr =~ s/\s//g;
538 my @names = split(',', $attr);
539
540 #There can be up to 4 entries in this attribute
541 for (my $i = 0; $i < scalar @names; $i += 2) {
542
543 #$names[$i] holds the name of the attribute.
544 #$names[$i+1] holds the name of the property to store its value in.
545 if (($names[$i] ne "NA") && ($names[$i] ne "")) {
546
547 my $val = $g_targetObj->getAttribute($chip, $names[$i]);
548
549 #if the value is empty, assume it's for a standalone property,
550 #which gets turned into: some-property;
551 if ($val eq "") {
552 $props{$names[$i+1]} = ZERO_LENGTH_PROPERTY;
553 }
554 else {
555 $props{$names[$i+1]} = "<$val>";
556 }
557 }
558 }
559
560 return %props;
561}
562
563
564#Convert the MRW I2C address into the format the dts needs
565# $addr = the I2C Address
566sub adjustI2CAddress()
567{
568 my $addr = shift;
569
570 #MRW holds the 8 bit value. We need the 7 bit one.
571 my $addr = $addr >> 1;
572 $addr = sprintf("0x%X", $addr);
573 $addr = lc $addr;
574
575 return $addr;
576}
577
578
Matt Spinler30b461c2016-10-10 16:50:07 -0500579#Sets the global $g_i2cBusAdjust from the configuration file.
580sub getI2CBusAdjust()
Matt Spinler74909132016-10-07 13:52:19 -0500581{
Matt Spinler30b461c2016-10-10 16:50:07 -0500582 if (exists $g_configuration{"i2c-bus-adjust"}) {
Matt Spinler74909132016-10-07 13:52:19 -0500583
Matt Spinler30b461c2016-10-10 16:50:07 -0500584 $g_i2cBusAdjust = $g_configuration{"i2c-bus-adjust"};
Matt Spinler74909132016-10-07 13:52:19 -0500585
Matt Spinler30b461c2016-10-10 16:50:07 -0500586 if (!looks_like_number($g_i2cBusAdjust)) {
587 die "ERROR: Invalid i2c-bus-adjust value $g_i2cBusAdjust " .
588 "found in config file.\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500589 }
590 }
Matt Spinler30b461c2016-10-10 16:50:07 -0500591 else {
592 $g_i2cBusAdjust = 0;
593 print "WARNING: No I2C Bus number adjustment done " .
594 "for this system.\n";
595 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500596}
597
598
599#Returns a list of compatible fields for the BMC itself.
600sub getBMCCompatibles()
601{
602 my @compats;
603
Matt Spinler23d47c22016-10-04 12:31:21 -0500604 #1st entry: <system mfgr>,<system name>-bmc
605 #2nd entry: <bmc mfgr>,<bmc model>
Matt Spinler7d381e12016-09-27 14:27:24 -0500606
Matt Spinler23d47c22016-10-04 12:31:21 -0500607 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() }) {
608 if ($g_targetObj->getType($target) eq "SYS") {
609 my $mfgr = $g_targetObj->getAttribute($target, "MANUFACTURER");
610 push @compats, lc "$mfgr,$g_systemName-bmc";
611 last;
612 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500613 }
614
615 push @compats, lc($g_bmcMfgr).",".lc($g_bmcModel);
616
617 return @compats;
618}
619
620
621#Returns a string for the system's BMC model property
622sub getSystemBMCModel()
623{
Matt Spinler995f2a22016-09-30 13:07:31 -0500624 #'<System> BMC'
Matt Spinler7d381e12016-09-27 14:27:24 -0500625 my $sys = lc $g_systemName;
626 $sys = uc(substr($sys, 0, 1)) . substr($sys, 1);
627
628 return $sys . " BMC";
629}
630
631
632#Prints a list of nodes at the same indent level
633# $f = file handle
634# $level = indent level (0,1,etc)
635# @nodes = array of node hashes to print, where the
636# key for the hash is the name of the node
637sub printNodes()
638{
639 my ($f, $level, @nodes) = @_;
640
641 foreach my $n (@nodes) {
642 my %node = %$n;
643
644 foreach my $name (sort keys %node) {
645 my %n = %{ $node{$name} };
646 printNode($f, $level, $name, %n);
647 }
648 }
649}
650
651
652#Print a single node and its children
653# $f = file handle
654# $level = indent level (0,1,etc)
655# $name = the name of the node - shows up as:
656# name { ...
657# %vals = The contents of the node, with the following options:
658# if the key is:
659# - 'DTSI_INCLUDE', then value gets turned into a #include
Matt Spinler995f2a22016-09-30 13:07:31 -0500660# - 'COMMENT', then value gets turned into a // comment
Matt Spinler74909132016-10-07 13:52:19 -0500661# - 'ZERO_LENGTH_PROPERTY' then value gets turned into: value;
Matt Spinler7d381e12016-09-27 14:27:24 -0500662#
663# If the value is:
664# - a hash - then that hash gets turned into a child node
665# where the key is the name of the child node
Matt Spinler995f2a22016-09-30 13:07:31 -0500666# - an array of hashes indicates an array of child nodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500667sub printNode()
668{
669 my ($f, $level, $name, %vals) = @_;
670 my $include = "";
671
672 if ($level == 0) {
673 $name = "&".$name;
674 }
675
Matt Spinler995f2a22016-09-30 13:07:31 -0500676 print $f "\n";
677
678 if (exists $vals{COMMENT}) {
679 my @lines = split('\n', $vals{COMMENT});
680 foreach my $l (@lines) {
681 print $f indent($level) . "// $l\n";
682 }
683 }
684
685 print $f indent($level) . "$name {\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500686
Matt Spinler74909132016-10-07 13:52:19 -0500687 #First print properties, then includes, then subnodes
688
689 #Print Properties
Matt Spinler7d381e12016-09-27 14:27:24 -0500690 foreach my $v (sort keys %vals) {
691
Matt Spinler995f2a22016-09-30 13:07:31 -0500692 next if ($v eq "COMMENT");
Matt Spinler74909132016-10-07 13:52:19 -0500693 next if ($v eq "DTSI_INCLUDE");
694 next if (ref($vals{$v}) eq "HASH");
695 next if (ref($vals{$v}) eq "ARRAY");
Matt Spinler995f2a22016-09-30 13:07:31 -0500696
Matt Spinler74909132016-10-07 13:52:19 -0500697 if ($vals{$v} ne ZERO_LENGTH_PROPERTY) {
698 printProperty($f, $level+1, $v, $vals{$v});
Matt Spinler7d381e12016-09-27 14:27:24 -0500699 }
Matt Spinler74909132016-10-07 13:52:19 -0500700 else {
701 printZeroLengthProperty($f, $level+1, $v);
702 }
703 }
704
705 #Print Includes
706 foreach my $v (sort keys %vals) {
707
708 if ($v eq "DTSI_INCLUDE") {
709 #print 1 include per line
710 my @incs = split(',', $vals{$v});
711 foreach my $i (@incs) {
712 print $f qq(#include "$i";\n);
713 }
714 }
715 }
716
717 #Print Nodes
718 foreach my $v (sort keys %vals) {
719
720 if (ref($vals{$v}) eq "HASH") {
Matt Spinler7d381e12016-09-27 14:27:24 -0500721 printNode($f, $level+1, $v, %{$vals{$v}});
722 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500723 #An array of nested nodes
724 elsif (ref($vals{$v}) eq "ARRAY") {
725 my @array = @{$vals{$v}};
726 &printNodes($f, $level+1, @array);
727 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500728 }
729
730 print $f indent($level) . "};\n";
731}
732
733
734#Prints a comma separated list of properties.
735#e.g. a = "b, c, d";
736# $f = file handle
737# $level = indent level (0,1,etc)
738# $name = name of property
739# @vals = list of property values
740sub printPropertyList()
741{
742 my ($f, $level, $name, @vals) = @_;
743
744 print $f indent($level) . "$name = ";
745
746 for (my $i = 0;$i < scalar @vals; $i++) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500747 print $f qq("$vals[$i]");
Matt Spinler7d381e12016-09-27 14:27:24 -0500748 if ($i < (scalar(@vals) - 1)) {
749 print $f ", ";
750 }
751 }
752 print $f ";\n"
753}
754
755
756#Prints a single property. e.g. a = "b";
757# $f = file handle
758# $level = indent level (0,1,etc)
759# $name = name of property
760# @vals = property values
761sub printProperty()
762{
763 my ($f, $level, $name, $val) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -0500764 my $quoteChar = qq(");
Matt Spinler23d47c22016-10-04 12:31:21 -0500765
Matt Spinler30b461c2016-10-10 16:50:07 -0500766 $val = convertReference($val);
Matt Spinler23d47c22016-10-04 12:31:21 -0500767
768 #properties with < > or single word aliases don't need quotes
769 if (($val =~ /<.*>/) || ($val =~ /^&\w+$/)) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500770 $quoteChar = "";
Matt Spinler23d47c22016-10-04 12:31:21 -0500771 }
772
Matt Spinler30b461c2016-10-10 16:50:07 -0500773 print $f indent($level) . "$name = $quoteChar$val$quoteChar;\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500774}
775
776
Matt Spinler30b461c2016-10-10 16:50:07 -0500777#Prints a zero length property e.g. some-property;
Matt Spinler7d381e12016-09-27 14:27:24 -0500778# $f = file handle
779# $level = indent level (0,1,etc)
780# $name = name of property
Matt Spinler30b461c2016-10-10 16:50:07 -0500781sub printZeroLengthProperty()
Matt Spinler7d381e12016-09-27 14:27:24 -0500782{
783 my ($f, $level, $name) = @_;
784 print $f indent($level) . "$name;\n";
785}
786
787
Matt Spinler30b461c2016-10-10 16:50:07 -0500788#Replace '(ref)' with '&'.
Matt Spinler7d381e12016-09-27 14:27:24 -0500789#Needed because Serverwiz doesn't properly escape '&'s in the XML,
Matt Spinler30b461c2016-10-10 16:50:07 -0500790#so the '(ref)' string is used to represent the reference
Matt Spinler7d381e12016-09-27 14:27:24 -0500791#specifier instead of '&'.
Matt Spinler30b461c2016-10-10 16:50:07 -0500792sub convertReference() {
Matt Spinler7d381e12016-09-27 14:27:24 -0500793 my $val = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -0500794 $val =~ s/\(ref\)/&/g;
Matt Spinler7d381e12016-09-27 14:27:24 -0500795 return $val
796}
797
798
799#Returns the target for the BMC chip.
800#Not worrying about multiple BMC systems for now.
801sub getBMCTarget()
802{
803 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() })
804 {
805 if ($g_targetObj->getType($target) eq "BMC") {
806 return $target;
807 }
808 }
809 return "";
810}
811
812
813#Prints the device tree version line.
814# $f = file handle
815sub printVersion()
816{
817 my $f = shift;
818 print $f VERSION."\n"
819}
820
821
822#Prints the #include line for pulling in an include file.
Matt Spinler30b461c2016-10-10 16:50:07 -0500823#The files to include come from the configuration file.
Matt Spinler7d381e12016-09-27 14:27:24 -0500824# $f = file handle
Matt Spinler30b461c2016-10-10 16:50:07 -0500825# $type = include type
Matt Spinler7d381e12016-09-27 14:27:24 -0500826sub printIncludes()
827{
Matt Spinler30b461c2016-10-10 16:50:07 -0500828 my ($f, $type) = @_;
829 my @includes = getIncludes($type);
Matt Spinler7d381e12016-09-27 14:27:24 -0500830
831 foreach my $i (@includes) {
832 #if a .dtsi, gets " ", otherwise < >
833 if ($i =~ /\.dtsi$/) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500834 $i = qq("$i");
Matt Spinler7d381e12016-09-27 14:27:24 -0500835 }
836 else {
Matt Spinler30b461c2016-10-10 16:50:07 -0500837 $i = "<$i>";
Matt Spinler7d381e12016-09-27 14:27:24 -0500838 }
Matt Spinler30b461c2016-10-10 16:50:07 -0500839 print $f "#include $i\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500840 }
841}
842
843
Matt Spinler30b461c2016-10-10 16:50:07 -0500844#Returns an array of include files found in the config file
845#for the type specified.
846# $type = the include type, which is the section name in the
847# YAML configuration file.
Matt Spinler7d381e12016-09-27 14:27:24 -0500848sub getIncludes()
849{
Matt Spinler30b461c2016-10-10 16:50:07 -0500850 my $type = shift;
Matt Spinler7d381e12016-09-27 14:27:24 -0500851 my @includes;
852
Matt Spinler30b461c2016-10-10 16:50:07 -0500853 #The config file may have a section but no includes
854 #listed in it, which is OK.
855 if ((exists $g_configuration{includes}{$type}) &&
856 (ref($g_configuration{includes}{$type}) eq "ARRAY")) {
Matt Spinler7d381e12016-09-27 14:27:24 -0500857
Matt Spinler30b461c2016-10-10 16:50:07 -0500858 @includes = @{$g_configuration{includes}{$type}};
Matt Spinler7d381e12016-09-27 14:27:24 -0500859 }
860
861 return @includes;
862}
863
Matt Spinler30b461c2016-10-10 16:50:07 -0500864
Matt Spinler74909132016-10-07 13:52:19 -0500865#Appends the first value of the 'reg' property
866#passed in to the name passed in to create the
867#full name for the node
868# $name = node name that will be appended to
869# $reg = the reg property values
870sub makeNodeName()
871{
872 my ($name, $reg) = @_;
873
874 $reg =~ s/<//g;
875 $reg =~ s/>//g;
876 my @vals = split(' ', $reg);
877
878 if (scalar @vals > 0) {
879 $vals[0] =~ s/0x//;
880 $name .= "@" . lc $vals[0];
881 }
882
883 return $name;
884}
885
Matt Spinler7d381e12016-09-27 14:27:24 -0500886
887#Prints the root node starting bracket.
888# $f = file handle
889sub printRootNodeStart() {
890 my $f = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -0500891 print $f qq(/ {\n);
Matt Spinler7d381e12016-09-27 14:27:24 -0500892}
893
894
895#Prints the root node ending bracket.
896# $f = file handle
897# $level = indent level (0,1,etc)
898sub printRootNodeEnd() {
899 my ($f, $level) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -0500900 print $f indent($level).qq(};\n);
Matt Spinler7d381e12016-09-27 14:27:24 -0500901}
902
903
904#Returns a string that can be used to indent based on the
905#level passed in. Each level is an additional 4 spaces.
906# $level = indent level (0,1,etc)
907sub indent() {
908 my $level = shift;
909 return ' ' x ($level * 4);
910}
911
912
Matt Spinler995f2a22016-09-30 13:07:31 -0500913#Will look for all the connections of the specified type coming from
914#any sub target of the specified target, instead of just 1 level down
915#like the Targets inteface does. Needed because sometimes we have
916#target->pingroup->sourceunit instead of just target->sourceunit
917# $target = the target to find connections off of
918# $bus = the bus type
919# $partType = destination part type, leave off if a don't care
920sub findConnections() {
921 my ($target, $bus, $partType) = @_;
922 my %allConnections;
923 my $i = 0;
924
925 #get the ones from target->child
926 my $connections = $g_targetObj->findConnections($target, $bus, $partType);
927 if ($connections ne "") {
928 foreach my $c (@{$connections->{CONN}}) {
929 $allConnections{CONN}[$i] = { %{$c} };
930 $i++;
931 }
932 }
933
934 #get everything deeper
935 my @children = getAllTargetChildren($target);
936 foreach my $c (@children) {
937 my $connections = $g_targetObj->findConnections($c, $bus, $partType);
938 if ($connections ne "") {
939
940 foreach my $c (@{$connections->{CONN}}) {
941 $allConnections{CONN}[$i] = { %{$c} };
942 $i++;
943 }
944 }
945 }
946
Matt Spinler23d47c22016-10-04 12:31:21 -0500947 #Match the Targets::findConnections return strategy
948 if (!keys %allConnections) {
949 return "";
950 }
951
Matt Spinler995f2a22016-09-30 13:07:31 -0500952 return \%allConnections;
953}
954
955#Returns every sub target, not just the 1st level children.
956# $target = the target to find the children of
957sub getAllTargetChildren()
958{
959 my $target = shift;
960 my @children;
961
962 my $targets = $g_targetObj->getTargetChildren($target);
963 if ($targets ne "") {
964
965 foreach my $t (@$targets) {
966 push @children, $t;
967 my @more = getAllTargetChildren($t);
968 push @children, @more;
969 }
970 }
971
972 return @children;
973}
974
975
Matt Spinler7d381e12016-09-27 14:27:24 -0500976sub printUsage
977{
Matt Spinler30b461c2016-10-10 16:50:07 -0500978 print "gen_devtree.pl -x [XML filename] -y [yaml config file] " .
979 "-o [output filename]\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500980 exit(1);
981}