blob: 234d7eb43f593a5472d8d92aec3ffc7b4b435082 [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 Spinler25d60bb2016-10-31 15:16:03 -050062printNodes($f, 1, getBMCFlashNodes());
Matt Spinler995f2a22016-09-30 13:07:31 -050063
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
Matt Spinler25d60bb2016-10-31 15:16:03 -0500210#Returns an array of hashes representing the device tree nodes for
211#the BMC flash. These nodes are BMC model specific because different
212#models can have different device drivers.
213sub getBMCFlashNodes()
Matt Spinler7d381e12016-09-27 14:27:24 -0500214{
Matt Spinler25d60bb2016-10-31 15:16:03 -0500215 my @nodes;
216
217 if ($g_bmcModel eq "AST2500") {
218 my %node = getAST2500BMCSPIFlashNode();
219 push @nodes, { %node };
220 }
221 else {
222 die "ERROR: No BMC SPI flash support yet for BMC model $g_bmcModel\n";
223 }
224
225 return @nodes;
226}
227
228
229#Returns a hash that represents the BMC SPI flash(es) by finding the SPI
230#connections that come from the unit tagged as BMC_CODE. The code also
231#looks in the config file for any additional properties to add. Supports
232#the hardware where the same SPI master unit can be wired to more than 1
233#flash (a chip select line is used to switch between them.) This is
234#specific to the ASPEED AST2500 hardware and device driver.
235#Will look like:
236# fmc {
237# status = "okay"
238# flash@0 {
239# ...
240# };
241# flash@1 {
242# ...
243# };
244sub getAST2500BMCSPIFlashNode()
245{
246 my %bmcFlash;
247 my $chipSelect = 0;
248 my $lastUnit = "";
249
250 my $connections = findConnections($g_bmc, "SPI", "FLASH");
251
252 if ($connections eq "") {
253 die "ERROR: No BMC SPI flashes found connected to the BMC\n";
254 }
255
256 $bmcFlash{fmc}{status} = "okay";
257
258 foreach my $spi (@{$connections->{CONN}}) {
259
260 #Looking for spi-masters with a function of 'BMC_CODE'.
261 #It's possible there are multiple flash chips here.
262 if (!$g_targetObj->isBadAttribute($spi->{SOURCE}, "SPI_FUNCTION")) {
263
264 my $function = $g_targetObj->getAttribute($spi->{SOURCE},
265 "SPI_FUNCTION");
266 if ($function eq "BMC_CODE") {
267
268 my $flashName = "flash@".$chipSelect;
269
270 $bmcFlash{fmc}{$flashName}{COMMENT} = "$spi->{SOURCE} ->\n" .
271 "$spi->{DEST_PARENT}";
272
273 $bmcFlash{fmc}{$flashName}{status} = "okay";
274
275 #Add in anything specified in the config file for this chip.
276 addBMCFlashConfigProperties(\%{$bmcFlash{fmc}{$flashName}},
277 $chipSelect);
278
279 #The code currently only supports the config where a chip
280 #select line is used to select between possibly multiple
281 #flash chips attached to the same SPI pins/unit. So we
282 #need to make sure if there are multiple chips found, that
283 #they are off of the same master unit.
284 if ($lastUnit eq "") {
285 $lastUnit = $spi->{SOURCE};
286 }
287 else {
288 if ($lastUnit ne $spi->{SOURCE}) {
289 die "ERROR: Currently only 1 spi-master unit is " .
290 "supported for BMC flash connections."
291 }
292 }
293
294 #Since we don't need anything chip select specific from the
295 #XML, we can just assign our own chip selects.
296 $chipSelect++;
297 }
298 }
299 }
300
301 if ($chipSelect == 0) {
302 die "ERROR: Didn't find any BMC flash chips connected";
303 }
304
305 return %bmcFlash;
306}
307
308
309#Looks in the bmc-flash-config section in the config file for the
310#chip select passed in to add any additional properties to the BMC
311#flash node.
312# $node = hash reference to the flash node
313# $cs = the flash chip select value
314sub addBMCFlashConfigProperties()
315{
316 my ($node, $cs) = @_;
317 my $section = "chip-select-$cs";
318
319 if (exists $g_configuration{"bmc-flash-config"}{$section}) {
320 foreach my $key (sort keys $g_configuration{"bmc-flash-config"}{$section}) {
321 $node->{$key} = $g_configuration{"bmc-flash-config"}{$section}{$key};
322 }
323 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500324}
325
326
327#Returns a hash that represents the leds node by finding all of the
328#GPIO connections to LEDs.
329#Node will look like:
330# leds {
331# <ledname> {
332# gpios = &gpio ASPEED_GPIO(x, y) GPIO_ACTIVE_xxx>
333# };
334# <another ledname> {
335# ...
336# }
337sub getLEDNode()
338{
339 my %leds;
340
341 $leds{compatible} = "gpio-led";
342
343 my $connections = findConnections($g_bmc, "GPIO", "LED");
344
345 if ($connections eq "") {
346 print "WARNING: No LEDs found connected to the BMC\n";
347 return %leds;
348 }
349
350 foreach my $gpio (@{$connections->{CONN}}) {
351 my %ledNode;
352
353 $ledNode{COMMENT} = "$gpio->{SOURCE} ->\n$gpio->{DEST_PARENT}";
354
355 #The node name will be the simplified LED name
356 my $name = $gpio->{DEST_PARENT};
357 $name =~ s/(-\d+$)//; #remove trailing position
358 $name =~ s/.*\///; #remove the front of the path
359
360 #For now only supports ASPEED.
361 if (uc($g_bmcMfgr) ne "ASPEED") {
362 die "ERROR: Unsupported BMC manufacturer $g_bmcMfgr\n";
363 }
364 my $num = $g_targetObj->getAttribute($gpio->{SOURCE}, "PIN_NUM");
365 my $macro = getAspeedGpioMacro($num);
366
367 #If it's active high or low
368 my $state = $g_targetObj->getAttribute($gpio->{DEST_PARENT}, "ON_STATE");
369 my $activeString = getGpioActiveString($state);
370
371 $ledNode{gpios} = "<&gpio $macro $activeString>";
372
373 $leds{$name} = { %ledNode };
374 }
375
376 return %leds;
377}
378
379
380#Returns a either GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW
381# $val = either a 1 or a 0 for active high or low
382sub getGpioActiveString() {
383 my $val = shift;
384
385 if ($val == 0) {
386 return "GPIO_ACTIVE_LOW";
387 }
388
389 return "GPIO_ACTIVE_HIGH";
390}
391
392
393#Turns a GPIO number into something like ASPEED_GPIO(A, 0) for the
394#ASPEED GPIO numbering scheme A[0-7] -> Z[0-7] and then starts at
395#AA[0-7] after that.
396# $num = the GPIO number
397sub getAspeedGpioMacro() {
398 my $num = shift;
399 my $char;
400 my $offset = $num % 8;
401 my $block = int($num / 8);
402
403 #If past Z, wraps to AA, AB, etc
404 if ((ord('A') + $block) > ord('Z')) {
405 #how far past Z?
406 $char = $block - (ord('Z') - ord('A'));
407
408 #Don't let it wrap twice
409 if ($char > (ord('Z') - ord('A') + 1)) {
410 die "ERROR: Invalid PIN_NUM value $num found for GPIO\n";
411 }
412
413 #start back at 'A' again, and convert to a character
414 $char = chr($char + ord('A') - 1);
415
416 #Add in a bonus 'A', to get something like AB
417 $char = "A".$char;
418 }
419 else {
420 $char = ord('A') + $block;
421 $char = chr($char);
422 }
423
424 return "ASPEED_GPIO($char, $offset)";
425}
426
427
428#Returns a list of hashes that represent the UART nodes on the BMC by
429#finding the UART connections.
430#Nodes will look like:
431# &uartX {
432# status = "okay"
433# }
434sub getUARTNodes()
435{
436 my @nodes;
437
Matt Spinler23d47c22016-10-04 12:31:21 -0500438 #Using U750 for legacy MRW reasons
439 my $connections = findConnections($g_bmc, "U750");
Matt Spinler995f2a22016-09-30 13:07:31 -0500440
441 if ($connections eq "") {
442 print "WARNING: No UART buses found connected to the BMC\n";
443 return @nodes;
444 }
445
446 foreach my $uart (@{$connections->{CONN}}) {
447 my %node;
448
449 my $num = $g_targetObj->getAttribute($uart->{SOURCE}, "CHIP_UNIT");
450 my $name = "uart$num";
451
452 $node{$name}{status} = "okay";
453 $node{$name}{COMMENT} = "$uart->{SOURCE} ->\n$uart->{DEST_PARENT}";
454
455 push @nodes, { %node };
456 }
457
Matt Spinler7d381e12016-09-27 14:27:24 -0500458 return @nodes;
459}
460
461
Matt Spinler995f2a22016-09-30 13:07:31 -0500462#Returns a list of hashes that represent the MAC (ethernet) nodes on the BMC
463#by finding the connections of type ETHERNET.
464#Nodes will look like:
465# &macX {
466# ...
467# }
468sub getMacNodes()
469{
470 my @nodes;
471
472 my $connections = findConnections($g_bmc, "ETHERNET");
473
474 if ($connections eq "") {
475 print "WARNING: No ethernet buses found connected to the BMC\n";
476 return @nodes;
477 }
478
479 foreach my $eth (@{$connections->{CONN}}) {
480 my %node;
481
482 my $num = $g_targetObj->getAttribute($eth->{SOURCE}, "CHIP_UNIT");
483 my $ncsi = $g_targetObj->getAttribute($eth->{SOURCE}, "NCSI_MODE");
484 my $hwChecksum = $g_targetObj->getAttribute($eth->{SOURCE},
485 "USE_HW_CHECKSUM");
486
487 my $name = "mac$num";
488 $node{$name}{status} = "okay";
489
490 if ($ncsi == 1) {
Matt Spinler74909132016-10-07 13:52:19 -0500491 $node{$name}{"use-ncsi"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500492 }
493 if ($hwChecksum == 0) {
Matt Spinler74909132016-10-07 13:52:19 -0500494 $node{$name}{"no-hw-checksum"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500495 }
496
497 $node{$name}{COMMENT} = "$eth->{SOURCE} ->\n$eth->{DEST_PARENT}";
498
499 push @nodes, { %node };
500 }
501
502 return @nodes;
503}
504
505
506#Returns a list of hashes that represent the virtual UART nodes
507#Node will look like:
508# &vuart {
509# status = "okay"
510# }
Matt Spinler7d381e12016-09-27 14:27:24 -0500511sub getVuartNodes()
512{
513 my @nodes;
514 my %node;
515
516 #For now, enable 1 node all the time.
Matt Spinler995f2a22016-09-30 13:07:31 -0500517 #TBD if this needs to be fixed
Matt Spinler7d381e12016-09-27 14:27:24 -0500518 $node{vuart}{status} = "okay";
519
520 push @nodes, { %node };
521
522 return @nodes;
523}
524
Matt Spinler74909132016-10-07 13:52:19 -0500525#Returns a list of hashes that represent the I2C device nodes.
526#There is 1 parent node for each bus, which then have subnodes
527#for each device on that bus. If a bus doesn't have any
528#attached devices, it doesn't need to show up.
529#The nodes will look like:
530# &i2c0 {
531# status = "okay"
532# device1@addr { (addr = 7 bit I2C address)
533# reg = <addr>
534# compatible = ...
535# ...
536# }
537# device2@addr {
538# reg = <addr>
539# ...
540# }
541# }
542# &i2c1 {
543# ...
544# }
545sub getI2CNodes()
546{
547 my @nodes;
548 my %busNodes;
549
550 my $connections = findConnections($g_bmc, "I2C");
551
552 if ($connections eq "") {
553 print "WARNING: No I2C buses found connected to the BMC\n";
554 return @nodes;
555 }
556
557 foreach my $i2c (@{$connections->{CONN}}) {
558
559 my %deviceNode, my $deviceName;
560
561 $deviceNode{COMMENT} = "$i2c->{SOURCE} ->\n$i2c->{DEST_PARENT}";
562
563 $deviceName = lc $i2c->{DEST_PARENT};
564 $deviceName =~ s/-\d+$//; #remove trailing position
565 $deviceName =~ s/.*\///; #remove the front of the path
566
567 #Get the I2C address
568 my $i2cAddress = $g_targetObj->getAttribute($i2c->{DEST}, "I2C_ADDRESS");
569 $i2cAddress = hex($i2cAddress);
570 if ($i2cAddress == 0) {
571 die "ERROR: Missing I2C address on $i2c->{DEST}\n";
572 }
573
574 #Put it in the format we want to print it in
575 $i2cAddress = adjustI2CAddress($i2cAddress);
576 $deviceNode{reg} = "<$i2cAddress>";
577
578 $deviceName = makeNodeName($deviceName, $deviceNode{reg});
579
580 #Get the I2C bus number
581 if ($g_targetObj->isBadAttribute($i2c->{SOURCE},
582 "I2C_PORT")) {
583 die "ERROR: I2C_PORT attribute in $i2c->{DEST_PARENT} " .
584 "is either missing or empty.\n";
585 }
586
587 my $busNum = $g_targetObj->getAttribute($i2c->{SOURCE}, "I2C_PORT");
588 if ($busNum =~ /0x/i) {
589 $busNum = hex($busNum);
590 }
591
592 #Convert the number to the Linux numbering scheme.
Matt Spinler30b461c2016-10-10 16:50:07 -0500593 $busNum += $g_i2cBusAdjust;
Matt Spinler74909132016-10-07 13:52:19 -0500594
595 #Get the compatible property
596 if ($g_targetObj->isBadAttribute($i2c->{DEST_PARENT},
597 "BMC_DT_COMPATIBLE")) {
598 die "ERROR: BMC_DT_COMPATIBLE attribute in $i2c->{DEST_PARENT} " .
599 "is either missing or empty.\n";
600 }
601
602 $deviceNode{compatible} = $g_targetObj->getAttribute(
603 $i2c->{DEST_PARENT},
604 "BMC_DT_COMPATIBLE");
605
606 #Get any other part specific properties, where the property
607 #names are actually defined in the XML.
608 my %props = getPartDefinedDTProperties($i2c->{DEST_PARENT});
609 foreach my $prop (sort keys %props) {
610 $deviceNode{$prop} = $props{$prop};
611 }
612
613 #busNodeName is the hash twice so when we loop
614 #below it doesn't get lost
615 my $busNodeName = "i2c$busNum";
616 $busNodes{$busNodeName}{$busNodeName}{status} = "okay";
617 $busNodes{$busNodeName}{$busNodeName}{$deviceName} = { %deviceNode };
618 }
619
620 #Each bus gets its own hash entry in the array
621 for my $b (sort keys %busNodes) {
622 push @nodes, { %{$busNodes{$b}} };
623 }
624
625 return @nodes;
626}
627
628
629#Returns a hash of property names and values that should be stored in
630#the device tree node for this device. The names of the properties and
631#the attributes to find their values in are stored in the
632#BMC_DT_ATTR_NAMES attribute in the chip.
633# $chip = the chip target
634sub getPartDefinedDTProperties()
635{
636 my $chip = shift;
637 my %props;
638
639 if ($g_targetObj->isBadAttribute($chip, "BMC_DT_ATTR_NAMES")) {
640 return %props;
641 }
642
643 my $attr = $g_targetObj->getAttribute($chip, "BMC_DT_ATTR_NAMES");
644 $attr =~ s/\s//g;
645 my @names = split(',', $attr);
646
647 #There can be up to 4 entries in this attribute
648 for (my $i = 0; $i < scalar @names; $i += 2) {
649
650 #$names[$i] holds the name of the attribute.
651 #$names[$i+1] holds the name of the property to store its value in.
652 if (($names[$i] ne "NA") && ($names[$i] ne "")) {
653
654 my $val = $g_targetObj->getAttribute($chip, $names[$i]);
655
656 #if the value is empty, assume it's for a standalone property,
657 #which gets turned into: some-property;
658 if ($val eq "") {
659 $props{$names[$i+1]} = ZERO_LENGTH_PROPERTY;
660 }
661 else {
662 $props{$names[$i+1]} = "<$val>";
663 }
664 }
665 }
666
667 return %props;
668}
669
670
671#Convert the MRW I2C address into the format the dts needs
672# $addr = the I2C Address
673sub adjustI2CAddress()
674{
675 my $addr = shift;
676
677 #MRW holds the 8 bit value. We need the 7 bit one.
678 my $addr = $addr >> 1;
679 $addr = sprintf("0x%X", $addr);
680 $addr = lc $addr;
681
682 return $addr;
683}
684
685
Matt Spinler30b461c2016-10-10 16:50:07 -0500686#Sets the global $g_i2cBusAdjust from the configuration file.
687sub getI2CBusAdjust()
Matt Spinler74909132016-10-07 13:52:19 -0500688{
Matt Spinler30b461c2016-10-10 16:50:07 -0500689 if (exists $g_configuration{"i2c-bus-adjust"}) {
Matt Spinler74909132016-10-07 13:52:19 -0500690
Matt Spinler30b461c2016-10-10 16:50:07 -0500691 $g_i2cBusAdjust = $g_configuration{"i2c-bus-adjust"};
Matt Spinler74909132016-10-07 13:52:19 -0500692
Matt Spinler30b461c2016-10-10 16:50:07 -0500693 if (!looks_like_number($g_i2cBusAdjust)) {
694 die "ERROR: Invalid i2c-bus-adjust value $g_i2cBusAdjust " .
695 "found in config file.\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500696 }
697 }
Matt Spinler30b461c2016-10-10 16:50:07 -0500698 else {
699 $g_i2cBusAdjust = 0;
700 print "WARNING: No I2C Bus number adjustment done " .
701 "for this system.\n";
702 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500703}
704
705
706#Returns a list of compatible fields for the BMC itself.
707sub getBMCCompatibles()
708{
709 my @compats;
710
Matt Spinler23d47c22016-10-04 12:31:21 -0500711 #1st entry: <system mfgr>,<system name>-bmc
712 #2nd entry: <bmc mfgr>,<bmc model>
Matt Spinler7d381e12016-09-27 14:27:24 -0500713
Matt Spinler23d47c22016-10-04 12:31:21 -0500714 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() }) {
715 if ($g_targetObj->getType($target) eq "SYS") {
716 my $mfgr = $g_targetObj->getAttribute($target, "MANUFACTURER");
717 push @compats, lc "$mfgr,$g_systemName-bmc";
718 last;
719 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500720 }
721
722 push @compats, lc($g_bmcMfgr).",".lc($g_bmcModel);
723
724 return @compats;
725}
726
727
728#Returns a string for the system's BMC model property
729sub getSystemBMCModel()
730{
Matt Spinler995f2a22016-09-30 13:07:31 -0500731 #'<System> BMC'
Matt Spinler7d381e12016-09-27 14:27:24 -0500732 my $sys = lc $g_systemName;
733 $sys = uc(substr($sys, 0, 1)) . substr($sys, 1);
734
735 return $sys . " BMC";
736}
737
738
739#Prints a list of nodes at the same indent level
740# $f = file handle
741# $level = indent level (0,1,etc)
742# @nodes = array of node hashes to print, where the
743# key for the hash is the name of the node
744sub printNodes()
745{
746 my ($f, $level, @nodes) = @_;
747
748 foreach my $n (@nodes) {
749 my %node = %$n;
750
751 foreach my $name (sort keys %node) {
752 my %n = %{ $node{$name} };
753 printNode($f, $level, $name, %n);
754 }
755 }
756}
757
758
759#Print a single node and its children
760# $f = file handle
761# $level = indent level (0,1,etc)
762# $name = the name of the node - shows up as:
763# name { ...
764# %vals = The contents of the node, with the following options:
765# if the key is:
766# - 'DTSI_INCLUDE', then value gets turned into a #include
Matt Spinler995f2a22016-09-30 13:07:31 -0500767# - 'COMMENT', then value gets turned into a // comment
Matt Spinler74909132016-10-07 13:52:19 -0500768# - 'ZERO_LENGTH_PROPERTY' then value gets turned into: value;
Matt Spinler7d381e12016-09-27 14:27:24 -0500769#
770# If the value is:
771# - a hash - then that hash gets turned into a child node
772# where the key is the name of the child node
Matt Spinler995f2a22016-09-30 13:07:31 -0500773# - an array of hashes indicates an array of child nodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500774sub printNode()
775{
776 my ($f, $level, $name, %vals) = @_;
777 my $include = "";
778
779 if ($level == 0) {
780 $name = "&".$name;
781 }
782
Matt Spinler995f2a22016-09-30 13:07:31 -0500783 print $f "\n";
784
785 if (exists $vals{COMMENT}) {
786 my @lines = split('\n', $vals{COMMENT});
787 foreach my $l (@lines) {
788 print $f indent($level) . "// $l\n";
789 }
790 }
791
792 print $f indent($level) . "$name {\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500793
Matt Spinler74909132016-10-07 13:52:19 -0500794 #First print properties, then includes, then subnodes
795
796 #Print Properties
Matt Spinler7d381e12016-09-27 14:27:24 -0500797 foreach my $v (sort keys %vals) {
798
Matt Spinler995f2a22016-09-30 13:07:31 -0500799 next if ($v eq "COMMENT");
Matt Spinler74909132016-10-07 13:52:19 -0500800 next if ($v eq "DTSI_INCLUDE");
801 next if (ref($vals{$v}) eq "HASH");
802 next if (ref($vals{$v}) eq "ARRAY");
Matt Spinler995f2a22016-09-30 13:07:31 -0500803
Matt Spinler74909132016-10-07 13:52:19 -0500804 if ($vals{$v} ne ZERO_LENGTH_PROPERTY) {
805 printProperty($f, $level+1, $v, $vals{$v});
Matt Spinler7d381e12016-09-27 14:27:24 -0500806 }
Matt Spinler74909132016-10-07 13:52:19 -0500807 else {
808 printZeroLengthProperty($f, $level+1, $v);
809 }
810 }
811
812 #Print Includes
813 foreach my $v (sort keys %vals) {
814
815 if ($v eq "DTSI_INCLUDE") {
816 #print 1 include per line
817 my @incs = split(',', $vals{$v});
818 foreach my $i (@incs) {
819 print $f qq(#include "$i";\n);
820 }
821 }
822 }
823
824 #Print Nodes
825 foreach my $v (sort keys %vals) {
826
827 if (ref($vals{$v}) eq "HASH") {
Matt Spinler7d381e12016-09-27 14:27:24 -0500828 printNode($f, $level+1, $v, %{$vals{$v}});
829 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500830 #An array of nested nodes
831 elsif (ref($vals{$v}) eq "ARRAY") {
832 my @array = @{$vals{$v}};
833 &printNodes($f, $level+1, @array);
834 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500835 }
836
837 print $f indent($level) . "};\n";
838}
839
840
841#Prints a comma separated list of properties.
842#e.g. a = "b, c, d";
843# $f = file handle
844# $level = indent level (0,1,etc)
845# $name = name of property
846# @vals = list of property values
847sub printPropertyList()
848{
849 my ($f, $level, $name, @vals) = @_;
850
851 print $f indent($level) . "$name = ";
852
853 for (my $i = 0;$i < scalar @vals; $i++) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500854 print $f qq("$vals[$i]");
Matt Spinler7d381e12016-09-27 14:27:24 -0500855 if ($i < (scalar(@vals) - 1)) {
856 print $f ", ";
857 }
858 }
859 print $f ";\n"
860}
861
862
863#Prints a single property. e.g. a = "b";
864# $f = file handle
865# $level = indent level (0,1,etc)
866# $name = name of property
867# @vals = property values
868sub printProperty()
869{
870 my ($f, $level, $name, $val) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -0500871 my $quoteChar = qq(");
Matt Spinler23d47c22016-10-04 12:31:21 -0500872
Matt Spinler30b461c2016-10-10 16:50:07 -0500873 $val = convertReference($val);
Matt Spinler23d47c22016-10-04 12:31:21 -0500874
875 #properties with < > or single word aliases don't need quotes
876 if (($val =~ /<.*>/) || ($val =~ /^&\w+$/)) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500877 $quoteChar = "";
Matt Spinler23d47c22016-10-04 12:31:21 -0500878 }
879
Matt Spinler30b461c2016-10-10 16:50:07 -0500880 print $f indent($level) . "$name = $quoteChar$val$quoteChar;\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500881}
882
883
Matt Spinler30b461c2016-10-10 16:50:07 -0500884#Prints a zero length property e.g. some-property;
Matt Spinler7d381e12016-09-27 14:27:24 -0500885# $f = file handle
886# $level = indent level (0,1,etc)
887# $name = name of property
Matt Spinler30b461c2016-10-10 16:50:07 -0500888sub printZeroLengthProperty()
Matt Spinler7d381e12016-09-27 14:27:24 -0500889{
890 my ($f, $level, $name) = @_;
891 print $f indent($level) . "$name;\n";
892}
893
894
Matt Spinler30b461c2016-10-10 16:50:07 -0500895#Replace '(ref)' with '&'.
Matt Spinler7d381e12016-09-27 14:27:24 -0500896#Needed because Serverwiz doesn't properly escape '&'s in the XML,
Matt Spinler30b461c2016-10-10 16:50:07 -0500897#so the '(ref)' string is used to represent the reference
Matt Spinler7d381e12016-09-27 14:27:24 -0500898#specifier instead of '&'.
Matt Spinler30b461c2016-10-10 16:50:07 -0500899sub convertReference() {
Matt Spinler7d381e12016-09-27 14:27:24 -0500900 my $val = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -0500901 $val =~ s/\(ref\)/&/g;
Matt Spinler7d381e12016-09-27 14:27:24 -0500902 return $val
903}
904
905
906#Returns the target for the BMC chip.
907#Not worrying about multiple BMC systems for now.
908sub getBMCTarget()
909{
910 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() })
911 {
912 if ($g_targetObj->getType($target) eq "BMC") {
913 return $target;
914 }
915 }
916 return "";
917}
918
919
920#Prints the device tree version line.
921# $f = file handle
922sub printVersion()
923{
924 my $f = shift;
925 print $f VERSION."\n"
926}
927
928
929#Prints the #include line for pulling in an include file.
Matt Spinler30b461c2016-10-10 16:50:07 -0500930#The files to include come from the configuration file.
Matt Spinler7d381e12016-09-27 14:27:24 -0500931# $f = file handle
Matt Spinler30b461c2016-10-10 16:50:07 -0500932# $type = include type
Matt Spinler7d381e12016-09-27 14:27:24 -0500933sub printIncludes()
934{
Matt Spinler30b461c2016-10-10 16:50:07 -0500935 my ($f, $type) = @_;
936 my @includes = getIncludes($type);
Matt Spinler7d381e12016-09-27 14:27:24 -0500937
938 foreach my $i (@includes) {
939 #if a .dtsi, gets " ", otherwise < >
940 if ($i =~ /\.dtsi$/) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500941 $i = qq("$i");
Matt Spinler7d381e12016-09-27 14:27:24 -0500942 }
943 else {
Matt Spinler30b461c2016-10-10 16:50:07 -0500944 $i = "<$i>";
Matt Spinler7d381e12016-09-27 14:27:24 -0500945 }
Matt Spinler30b461c2016-10-10 16:50:07 -0500946 print $f "#include $i\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500947 }
948}
949
950
Matt Spinler30b461c2016-10-10 16:50:07 -0500951#Returns an array of include files found in the config file
952#for the type specified.
953# $type = the include type, which is the section name in the
954# YAML configuration file.
Matt Spinler7d381e12016-09-27 14:27:24 -0500955sub getIncludes()
956{
Matt Spinler30b461c2016-10-10 16:50:07 -0500957 my $type = shift;
Matt Spinler7d381e12016-09-27 14:27:24 -0500958 my @includes;
959
Matt Spinler30b461c2016-10-10 16:50:07 -0500960 #The config file may have a section but no includes
961 #listed in it, which is OK.
962 if ((exists $g_configuration{includes}{$type}) &&
963 (ref($g_configuration{includes}{$type}) eq "ARRAY")) {
Matt Spinler7d381e12016-09-27 14:27:24 -0500964
Matt Spinler30b461c2016-10-10 16:50:07 -0500965 @includes = @{$g_configuration{includes}{$type}};
Matt Spinler7d381e12016-09-27 14:27:24 -0500966 }
967
968 return @includes;
969}
970
Matt Spinler30b461c2016-10-10 16:50:07 -0500971
Matt Spinler74909132016-10-07 13:52:19 -0500972#Appends the first value of the 'reg' property
973#passed in to the name passed in to create the
974#full name for the node
975# $name = node name that will be appended to
976# $reg = the reg property values
977sub makeNodeName()
978{
979 my ($name, $reg) = @_;
980
981 $reg =~ s/<//g;
982 $reg =~ s/>//g;
983 my @vals = split(' ', $reg);
984
985 if (scalar @vals > 0) {
986 $vals[0] =~ s/0x//;
987 $name .= "@" . lc $vals[0];
988 }
989
990 return $name;
991}
992
Matt Spinler7d381e12016-09-27 14:27:24 -0500993
994#Prints the root node starting bracket.
995# $f = file handle
996sub printRootNodeStart() {
997 my $f = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -0500998 print $f qq(/ {\n);
Matt Spinler7d381e12016-09-27 14:27:24 -0500999}
1000
1001
1002#Prints the root node ending bracket.
1003# $f = file handle
1004# $level = indent level (0,1,etc)
1005sub printRootNodeEnd() {
1006 my ($f, $level) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001007 print $f indent($level).qq(};\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001008}
1009
1010
1011#Returns a string that can be used to indent based on the
1012#level passed in. Each level is an additional 4 spaces.
1013# $level = indent level (0,1,etc)
1014sub indent() {
1015 my $level = shift;
1016 return ' ' x ($level * 4);
1017}
1018
1019
Matt Spinler995f2a22016-09-30 13:07:31 -05001020#Will look for all the connections of the specified type coming from
1021#any sub target of the specified target, instead of just 1 level down
1022#like the Targets inteface does. Needed because sometimes we have
1023#target->pingroup->sourceunit instead of just target->sourceunit
1024# $target = the target to find connections off of
1025# $bus = the bus type
1026# $partType = destination part type, leave off if a don't care
1027sub findConnections() {
1028 my ($target, $bus, $partType) = @_;
1029 my %allConnections;
1030 my $i = 0;
1031
1032 #get the ones from target->child
1033 my $connections = $g_targetObj->findConnections($target, $bus, $partType);
1034 if ($connections ne "") {
1035 foreach my $c (@{$connections->{CONN}}) {
1036 $allConnections{CONN}[$i] = { %{$c} };
1037 $i++;
1038 }
1039 }
1040
1041 #get everything deeper
1042 my @children = getAllTargetChildren($target);
1043 foreach my $c (@children) {
1044 my $connections = $g_targetObj->findConnections($c, $bus, $partType);
1045 if ($connections ne "") {
1046
1047 foreach my $c (@{$connections->{CONN}}) {
1048 $allConnections{CONN}[$i] = { %{$c} };
1049 $i++;
1050 }
1051 }
1052 }
1053
Matt Spinler23d47c22016-10-04 12:31:21 -05001054 #Match the Targets::findConnections return strategy
1055 if (!keys %allConnections) {
1056 return "";
1057 }
1058
Matt Spinler995f2a22016-09-30 13:07:31 -05001059 return \%allConnections;
1060}
1061
1062#Returns every sub target, not just the 1st level children.
1063# $target = the target to find the children of
1064sub getAllTargetChildren()
1065{
1066 my $target = shift;
1067 my @children;
1068
1069 my $targets = $g_targetObj->getTargetChildren($target);
1070 if ($targets ne "") {
1071
1072 foreach my $t (@$targets) {
1073 push @children, $t;
1074 my @more = getAllTargetChildren($t);
1075 push @children, @more;
1076 }
1077 }
1078
1079 return @children;
1080}
1081
1082
Matt Spinler7d381e12016-09-27 14:27:24 -05001083sub printUsage
1084{
Matt Spinler30b461c2016-10-10 16:50:07 -05001085 print "gen_devtree.pl -x [XML filename] -y [yaml config file] " .
1086 "-o [output filename]\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001087 exit(1);
1088}