blob: 50c9a7744a54b62fabf6872ca1306bb3ec42544e [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/;";
Matt Spinler74909132016-10-07 13:52:19 -050012use constant ZERO_LENGTH_PROPERTY => "zero_length_property";
13
14#For converting MRW I2C Bus numbers to Linux relative ones.
15my %I2C_BUS_ADJUST = (
16 AST2400 => -1,
17 AST2500 => -1
18);
19
Matt Spinler7d381e12016-09-27 14:27:24 -050020
21my $serverwizFile;
22my $outputFile;
23my $debug;
24
25GetOptions("x=s" => \$serverwizFile,
26 "o=s" => \$outputFile,
27 "d" => \$debug)
28or printUsage();
29
30if ((not defined $serverwizFile) || (not defined $outputFile)) {
31 printUsage();
32}
33
34my $g_targetObj = Targets->new;
35$g_targetObj->loadXML($serverwizFile);
36
Matt Spinler74909132016-10-07 13:52:19 -050037my ($g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName);
38setGlobalAttributes();
Matt Spinler7d381e12016-09-27 14:27:24 -050039
Matt Spinler7d381e12016-09-27 14:27:24 -050040
41open (my $f, ">$outputFile") or die "Could not open $outputFile\n";
42
43printVersion($f);
44printIncludes($f, 0);
45printRootNodeStart($f);
46
47printPropertyList($f, 1, "model", getSystemBMCModel());
Matt Spinler7d381e12016-09-27 14:27:24 -050048printPropertyList($f, 1, "compatible", getBMCCompatibles());
Matt Spinler995f2a22016-09-30 13:07:31 -050049
Matt Spinler23d47c22016-10-04 12:31:21 -050050printNode($f, 1, "aliases", getAliases());
Matt Spinler7d381e12016-09-27 14:27:24 -050051printNode($f, 1, "chosen", getChosen());
52printNode($f, 1, "memory", getMemory($g_bmc));
53
Matt Spinler995f2a22016-09-30 13:07:31 -050054printNodes($f, 1, getSpiFlashNodes());
55
56printNode($f, 1, "leds", getLEDNode());
57
Matt Spinler7d381e12016-09-27 14:27:24 -050058printRootNodeEnd($f, 0);
59
Matt Spinler74909132016-10-07 13:52:19 -050060printNodes($f, 0, getI2CNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050061printNodes($f, 0, getMacNodes());
Matt Spinler995f2a22016-09-30 13:07:31 -050062printNodes($f, 0, getUARTNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050063printNodes($f, 0, getVuartNodes());
64
65close $f;
66exit 0;
67
68
Matt Spinler74909132016-10-07 13:52:19 -050069#Finds the values for these globals:
70# $g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName
71sub setGlobalAttributes()
72{
73 $g_bmc = getBMCTarget();
74 if (length($g_bmc) == 0) {
75 die "Unable to find a BMC in this system\n";
76 }
77
78 if ($g_targetObj->isBadAttribute($g_bmc, "MODEL")) {
79 die "The MODEL attribute on $g_bmc is missing or empty.\n";
80 }
81 $g_bmcModel = $g_targetObj->getAttribute($g_bmc, "MODEL");
82
83 if ($g_targetObj->isBadAttribute($g_bmc, "MANUFACTURER")) {
84 die "The MANUFACTURER attribute on $g_bmc is missing or empty.\n";
85 }
86 $g_bmcMfgr = $g_targetObj->getAttribute($g_bmc, "MANUFACTURER");
87
88 $g_systemName = $g_targetObj->getSystemName();
89 if (length($g_systemName) == 0) {
90 die "The SYSTEM_NAME attribute is not set on the system target.\n";
91 }
92}
93
94
Matt Spinler23d47c22016-10-04 12:31:21 -050095#Returns a hash that represents the 'aliases' node.
96#Will look like:
97# aliases {
98# name1 = &val1;
99# name2 = &val2;
100# ...
101# }
102sub getAliases()
103{
104 my %aliases;
105 my $name, my $val;
106
107 #The MRW supports up to 6 name and value pairs.
108 for (my $i = 1; $i <= 6; $i++) {
109 my $nameAttr = "name$i";
110 my $valAttr = "value$i";
111
112 $name = $g_targetObj->getAttributeField($g_bmc, "BMC_DT_ALIASES",
113 $nameAttr);
114 if ($name ne "") {
115 $val = $g_targetObj->getAttributeField($g_bmc, "BMC_DT_ALIASES",
116 $valAttr);
117 #The value will be printed as '&val'
118 $aliases{$name} = "(alias)$val";
119 }
120 }
121
122 return %aliases;
123}
124
Matt Spinler7d381e12016-09-27 14:27:24 -0500125
126#Return a hash that represents the 'chosen' node
Matt Spinler995f2a22016-09-30 13:07:31 -0500127#Will look like:
128# chosen {
129# stdout-path = ...
130# bootargs = ...
131# }
Matt Spinler7d381e12016-09-27 14:27:24 -0500132sub getChosen()
133{
134 my $bmcStdOut = $g_targetObj->getAttributeField($g_bmc, "BMC_DT_CHOSEN",
135 "stdout-path");
136 my $args = $g_targetObj->getAttributeField($g_bmc, "BMC_DT_CHOSEN",
137 "bootargs");
138 my %chosen;
139 $chosen{"stdout-path"} = $bmcStdOut;
140 $chosen{"bootargs"} = $args;
141 return %chosen;
142}
143
144
Matt Spinler995f2a22016-09-30 13:07:31 -0500145#Gets the nodes that represents the BMC's SPI flash chips. They're based
146#on information from the spi-master-unit end of the connection, with
147#a subnode of information from the destination chip.
148#On ASPEED chips, they're nested under the ahb node (Advanced
149#High-performance Bus).
150#Will look like:
151# ahb {
152# fmc@... {
153# reg = ...
154# #address-cells = ...
155# #size-cells = ...
156# #compatible = ...
157#
158# flash@... {
159# reg = ...
160# compatible = ...
161# label = ...
162# #include ...
163# }
164# }
165# spi@... {
166# ...
167# }
168# }
169sub getSpiFlashNodes()
Matt Spinler7d381e12016-09-27 14:27:24 -0500170{
Matt Spinler995f2a22016-09-30 13:07:31 -0500171 my %parentNode, my %node, my @nodes;
172 my $lastParentNodeName = "default";
173 my $parentNodeName = "ahb";
Matt Spinler7d381e12016-09-27 14:27:24 -0500174
Matt Spinler995f2a22016-09-30 13:07:31 -0500175 my $connections = findConnections($g_bmc, "SPI", "FLASH");
176 if ($connections eq "") {
177 print "WARNING: No SPI flashes found connected to the BMC\n";
178 return @nodes;
179 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500180
Matt Spinler995f2a22016-09-30 13:07:31 -0500181 foreach my $spi (@{$connections->{CONN}}) {
Matt Spinler7d381e12016-09-27 14:27:24 -0500182
Matt Spinler995f2a22016-09-30 13:07:31 -0500183 my %unitNode; #Node for the SPI master unit
184 my %flashNode; #subnode for the flash chip itself
185 my $flashNodeName = "flash";
186 my $nodeLabel = "";
187 my @addresses;
Matt Spinler7d381e12016-09-27 14:27:24 -0500188
Matt Spinler7d381e12016-09-27 14:27:24 -0500189
Matt Spinler995f2a22016-09-30 13:07:31 -0500190 #Adds a comment into the output file about the MRW connection
191 #that makes up this node. Not that {SOURCE} always represents
192 #the master unit, and DEST_PARENT represents the destination
193 #chip. The destination unit {DEST} isn't usually that interesting.
194 $unitNode{COMMENT} = "$spi->{SOURCE} ->\n$spi->{DEST_PARENT}";
195
196 #These flashes are nested in the 'ahb' (an internal chip bus)
197 #node in ASPEED chips. Get the name of it here. Will default
198 #to 'ahb' if not set.
199 if (!$g_targetObj->isBadAttribute($spi->{SOURCE},
200 "INTERNAL_BUS", "NA")) {
201 $parentNodeName = $g_targetObj->getAttribute($spi->{SOURCE},
202 "INTERNAL_BUS");
203 #Not going to support this unless we have to
204 if ($parentNodeName != $lastParentNodeName) {
205 die "ERROR: SPI master unit $spi->{SOURCE} has a " .
206 "different internal bus name $parentNodeName than " .
207 "previous name $lastParentNodeName\n";
208 }
209 else {
210 $lastParentNodeName = $parentNodeName;
Matt Spinler7d381e12016-09-27 14:27:24 -0500211 }
212 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500213 else {
214 print "WARNING: No INTERNAL_BUS attribute value found for " .
215 "SPI flash unit $spi->{SOURCE}. Using '$parentNodeName'\n";
216 }
217
218 #The reg base and size of the unit will be added into
219 #the reg property
220 my $regBase = $g_targetObj->getAttribute($spi->{SOURCE},
221 "BMC_DT_REG_BASE");
222 my $regSize = $g_targetObj->getAttribute($spi->{SOURCE},
223 "BMC_DT_REG_SIZE");
224
225 #There is also another memory range that goes into reg
226 my %sourceRegHash = getMemory($spi->{SOURCE});
227
228 #Insert the regBase and regSize to the memory < ... > property
229 $unitNode{reg} = "< $regBase $regSize " . substr($sourceRegHash{reg}, 2);
230
231 #usually, this will be something like 'smc' or 'spi'
232 my $nodeName = "spi";
233 if (!$g_targetObj->isBadAttribute($spi->{SOURCE},
234 "BMC_DT_NODE_NAME")) {
235 $nodeName = $g_targetObj->getAttribute($spi->{SOURCE},
236 "BMC_DT_NODE_NAME");
237 }
238 else {
239 print "WARNING: No BMC_DT_NODE_NAME attribute value found for " .
240 "SPI flash unit $spi->{SOURCE}. Using 'spi'\n";
241 }
242
243 #now turn it into something like fmc@...
Matt Spinler23d47c22016-10-04 12:31:21 -0500244 $regBase =~ s/^0x//;
Matt Spinler995f2a22016-09-30 13:07:31 -0500245 $nodeName .= "@".$regBase;
246
247 if (!$g_targetObj->isBadAttribute($spi->{SOURCE},
248 "BMC_DT_COMPATIBLE")) {
249 $unitNode{compatible} = $g_targetObj->
250 getAttribute($spi->{SOURCE}, "BMC_DT_COMPATIBLE");
251 }
252 else {
253 print "WARNING: No BMC_DT_COMPATIBLE attribute found for SPI " .
254 "flash unit $spi->{SOURCE}\n";
255 }
256
257 #The flash chip has its one reg property as well
258 if (!$g_targetObj->isBadAttribute($spi->{DEST_PARENT},
259 "BMC_DT_REG_PROPERTY")) {
260 $flashNode{reg} = $g_targetObj->getAttribute($spi->{DEST_PARENT},
261 "BMC_DT_REG_PROPERTY");
262 $flashNode{reg} = "<" . $flashNode{reg} . ">";
263 }
264 else {
265 print "WARNING: No BMC_REG_PROPERTY attribute found for SPI " .
266 "flash $spi->{DEST_PARENT}. Using <0>.\n";
267 $flashNode{reg} = "<0>";
268 }
269
270 if (!$g_targetObj->isBadAttribute($spi->{DEST_PARENT},
271 "BMC_DT_COMPATIBLE")) {
272 $flashNode{compatible} = $g_targetObj->
273 getAttribute($spi->{DEST_PARENT}, "BMC_DT_COMPATIBLE");
274 }
275 else {
276 print "WARNING: No BMC_DT_COMPATIBLE attribute found for SPI " .
277 "flash $spi->{DEST_PARENT}\n";
278 }
279
280 if (!$g_targetObj->isBadAttribute($spi->{DEST_PARENT},
281 "BMC_DT_LABEL_PROPERTY")) {
282 $flashNode{label} = $g_targetObj->
283 getAttribute($spi->{DEST_PARENT}, "BMC_DT_LABEL_PROPERTY");
284 }
285
286 #Some flash chips have a .dtsi include to pull in more properties.
287 #Future - contents of the includes could be pulled into the MRW
288 #as new attributes.
289 if (!$g_targetObj->isBadAttribute($spi->{DEST_PARENT},
290 "BMC_DT_INCLUDES")) {
291 my $incs = $g_targetObj->
292 getAttribute($spi->{DEST_PARENT}, "BMC_DT_INCLUDES");
293 #first remove the spaces and NAs
294 $incs =~ s/\s+//g;
295 $incs =~ s/NA,*//g;
296 $flashNode{DTSI_INCLUDE} = $incs;
297 }
298
299 #the flash subnode name also has its reg[0] appended
300 #like flash@...
301 @addresses = split(' ', $flashNode{reg});
302 $addresses[0] =~ s/<//;
303 $addresses[0] =~ s/>//;
304 $flashNodeName .= "@" . $addresses[0];
305 $unitNode{$flashNodeName} = { %flashNode };
306
307 #For now, just support a chip with 1 reg value
308 if (scalar @addresses == 1) {
309 $unitNode{'#address-cells'} = "<1>";
310 $unitNode{'#size-cells'} = "<0>";
311 }
312 else {
313 die "ERROR: Unsupported number of <reg> entries " .
314 "in flash node $flashNodeName for SPI flash " .
315 "$spi->{DEST_PARENT}. Only 1 entry supported.\n";
316 }
317
318 #This node will end up being in an array on the parent node
319 my %node;
320 $node{$nodeName} = { %unitNode };
321 push @nodes, { %node };
Matt Spinler7d381e12016-09-27 14:27:24 -0500322 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500323
324 $parentNode{$parentNodeName}{nodes} = [ @nodes ];
325
326 #There is always just one in the array
327 my @finalNodes;
328 push @finalNodes, { %parentNode };
329 return @finalNodes;
330}
331
332
333#Returns a hash that represents the leds node by finding all of the
334#GPIO connections to LEDs.
335#Node will look like:
336# leds {
337# <ledname> {
338# gpios = &gpio ASPEED_GPIO(x, y) GPIO_ACTIVE_xxx>
339# };
340# <another ledname> {
341# ...
342# }
343sub getLEDNode()
344{
345 my %leds;
346
347 $leds{compatible} = "gpio-led";
348
349 my $connections = findConnections($g_bmc, "GPIO", "LED");
350
351 if ($connections eq "") {
352 print "WARNING: No LEDs found connected to the BMC\n";
353 return %leds;
354 }
355
356 foreach my $gpio (@{$connections->{CONN}}) {
357 my %ledNode;
358
359 $ledNode{COMMENT} = "$gpio->{SOURCE} ->\n$gpio->{DEST_PARENT}";
360
361 #The node name will be the simplified LED name
362 my $name = $gpio->{DEST_PARENT};
363 $name =~ s/(-\d+$)//; #remove trailing position
364 $name =~ s/.*\///; #remove the front of the path
365
366 #For now only supports ASPEED.
367 if (uc($g_bmcMfgr) ne "ASPEED") {
368 die "ERROR: Unsupported BMC manufacturer $g_bmcMfgr\n";
369 }
370 my $num = $g_targetObj->getAttribute($gpio->{SOURCE}, "PIN_NUM");
371 my $macro = getAspeedGpioMacro($num);
372
373 #If it's active high or low
374 my $state = $g_targetObj->getAttribute($gpio->{DEST_PARENT}, "ON_STATE");
375 my $activeString = getGpioActiveString($state);
376
377 $ledNode{gpios} = "<&gpio $macro $activeString>";
378
379 $leds{$name} = { %ledNode };
380 }
381
382 return %leds;
383}
384
385
386#Returns a either GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW
387# $val = either a 1 or a 0 for active high or low
388sub getGpioActiveString() {
389 my $val = shift;
390
391 if ($val == 0) {
392 return "GPIO_ACTIVE_LOW";
393 }
394
395 return "GPIO_ACTIVE_HIGH";
396}
397
398
399#Turns a GPIO number into something like ASPEED_GPIO(A, 0) for the
400#ASPEED GPIO numbering scheme A[0-7] -> Z[0-7] and then starts at
401#AA[0-7] after that.
402# $num = the GPIO number
403sub getAspeedGpioMacro() {
404 my $num = shift;
405 my $char;
406 my $offset = $num % 8;
407 my $block = int($num / 8);
408
409 #If past Z, wraps to AA, AB, etc
410 if ((ord('A') + $block) > ord('Z')) {
411 #how far past Z?
412 $char = $block - (ord('Z') - ord('A'));
413
414 #Don't let it wrap twice
415 if ($char > (ord('Z') - ord('A') + 1)) {
416 die "ERROR: Invalid PIN_NUM value $num found for GPIO\n";
417 }
418
419 #start back at 'A' again, and convert to a character
420 $char = chr($char + ord('A') - 1);
421
422 #Add in a bonus 'A', to get something like AB
423 $char = "A".$char;
424 }
425 else {
426 $char = ord('A') + $block;
427 $char = chr($char);
428 }
429
430 return "ASPEED_GPIO($char, $offset)";
431}
432
433
434#Returns a list of hashes that represent the UART nodes on the BMC by
435#finding the UART connections.
436#Nodes will look like:
437# &uartX {
438# status = "okay"
439# }
440sub getUARTNodes()
441{
442 my @nodes;
443
Matt Spinler23d47c22016-10-04 12:31:21 -0500444 #Using U750 for legacy MRW reasons
445 my $connections = findConnections($g_bmc, "U750");
Matt Spinler995f2a22016-09-30 13:07:31 -0500446
447 if ($connections eq "") {
448 print "WARNING: No UART buses found connected to the BMC\n";
449 return @nodes;
450 }
451
452 foreach my $uart (@{$connections->{CONN}}) {
453 my %node;
454
455 my $num = $g_targetObj->getAttribute($uart->{SOURCE}, "CHIP_UNIT");
456 my $name = "uart$num";
457
458 $node{$name}{status} = "okay";
459 $node{$name}{COMMENT} = "$uart->{SOURCE} ->\n$uart->{DEST_PARENT}";
460
461 push @nodes, { %node };
462 }
463
Matt Spinler7d381e12016-09-27 14:27:24 -0500464 return @nodes;
465}
466
467
Matt Spinler995f2a22016-09-30 13:07:31 -0500468#Returns a list of hashes that represent the MAC (ethernet) nodes on the BMC
469#by finding the connections of type ETHERNET.
470#Nodes will look like:
471# &macX {
472# ...
473# }
474sub getMacNodes()
475{
476 my @nodes;
477
478 my $connections = findConnections($g_bmc, "ETHERNET");
479
480 if ($connections eq "") {
481 print "WARNING: No ethernet buses found connected to the BMC\n";
482 return @nodes;
483 }
484
485 foreach my $eth (@{$connections->{CONN}}) {
486 my %node;
487
488 my $num = $g_targetObj->getAttribute($eth->{SOURCE}, "CHIP_UNIT");
489 my $ncsi = $g_targetObj->getAttribute($eth->{SOURCE}, "NCSI_MODE");
490 my $hwChecksum = $g_targetObj->getAttribute($eth->{SOURCE},
491 "USE_HW_CHECKSUM");
492
493 my $name = "mac$num";
494 $node{$name}{status} = "okay";
495
496 if ($ncsi == 1) {
Matt Spinler74909132016-10-07 13:52:19 -0500497 $node{$name}{"use-ncsi"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500498 }
499 if ($hwChecksum == 0) {
Matt Spinler74909132016-10-07 13:52:19 -0500500 $node{$name}{"no-hw-checksum"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500501 }
502
503 $node{$name}{COMMENT} = "$eth->{SOURCE} ->\n$eth->{DEST_PARENT}";
504
505 push @nodes, { %node };
506 }
507
508 return @nodes;
509}
510
511
512#Returns a list of hashes that represent the virtual UART nodes
513#Node will look like:
514# &vuart {
515# status = "okay"
516# }
Matt Spinler7d381e12016-09-27 14:27:24 -0500517sub getVuartNodes()
518{
519 my @nodes;
520 my %node;
521
522 #For now, enable 1 node all the time.
Matt Spinler995f2a22016-09-30 13:07:31 -0500523 #TBD if this needs to be fixed
Matt Spinler7d381e12016-09-27 14:27:24 -0500524 $node{vuart}{status} = "okay";
525
526 push @nodes, { %node };
527
528 return @nodes;
529}
530
Matt Spinler74909132016-10-07 13:52:19 -0500531#Returns a list of hashes that represent the I2C device nodes.
532#There is 1 parent node for each bus, which then have subnodes
533#for each device on that bus. If a bus doesn't have any
534#attached devices, it doesn't need to show up.
535#The nodes will look like:
536# &i2c0 {
537# status = "okay"
538# device1@addr { (addr = 7 bit I2C address)
539# reg = <addr>
540# compatible = ...
541# ...
542# }
543# device2@addr {
544# reg = <addr>
545# ...
546# }
547# }
548# &i2c1 {
549# ...
550# }
551sub getI2CNodes()
552{
553 my @nodes;
554 my %busNodes;
555
556 my $connections = findConnections($g_bmc, "I2C");
557
558 if ($connections eq "") {
559 print "WARNING: No I2C buses found connected to the BMC\n";
560 return @nodes;
561 }
562
563 foreach my $i2c (@{$connections->{CONN}}) {
564
565 my %deviceNode, my $deviceName;
566
567 $deviceNode{COMMENT} = "$i2c->{SOURCE} ->\n$i2c->{DEST_PARENT}";
568
569 $deviceName = lc $i2c->{DEST_PARENT};
570 $deviceName =~ s/-\d+$//; #remove trailing position
571 $deviceName =~ s/.*\///; #remove the front of the path
572
573 #Get the I2C address
574 my $i2cAddress = $g_targetObj->getAttribute($i2c->{DEST}, "I2C_ADDRESS");
575 $i2cAddress = hex($i2cAddress);
576 if ($i2cAddress == 0) {
577 die "ERROR: Missing I2C address on $i2c->{DEST}\n";
578 }
579
580 #Put it in the format we want to print it in
581 $i2cAddress = adjustI2CAddress($i2cAddress);
582 $deviceNode{reg} = "<$i2cAddress>";
583
584 $deviceName = makeNodeName($deviceName, $deviceNode{reg});
585
586 #Get the I2C bus number
587 if ($g_targetObj->isBadAttribute($i2c->{SOURCE},
588 "I2C_PORT")) {
589 die "ERROR: I2C_PORT attribute in $i2c->{DEST_PARENT} " .
590 "is either missing or empty.\n";
591 }
592
593 my $busNum = $g_targetObj->getAttribute($i2c->{SOURCE}, "I2C_PORT");
594 if ($busNum =~ /0x/i) {
595 $busNum = hex($busNum);
596 }
597
598 #Convert the number to the Linux numbering scheme.
599 $busNum = adjustI2CBusNum($busNum);
600
601 #Get the compatible property
602 if ($g_targetObj->isBadAttribute($i2c->{DEST_PARENT},
603 "BMC_DT_COMPATIBLE")) {
604 die "ERROR: BMC_DT_COMPATIBLE attribute in $i2c->{DEST_PARENT} " .
605 "is either missing or empty.\n";
606 }
607
608 $deviceNode{compatible} = $g_targetObj->getAttribute(
609 $i2c->{DEST_PARENT},
610 "BMC_DT_COMPATIBLE");
611
612 #Get any other part specific properties, where the property
613 #names are actually defined in the XML.
614 my %props = getPartDefinedDTProperties($i2c->{DEST_PARENT});
615 foreach my $prop (sort keys %props) {
616 $deviceNode{$prop} = $props{$prop};
617 }
618
619 #busNodeName is the hash twice so when we loop
620 #below it doesn't get lost
621 my $busNodeName = "i2c$busNum";
622 $busNodes{$busNodeName}{$busNodeName}{status} = "okay";
623 $busNodes{$busNodeName}{$busNodeName}{$deviceName} = { %deviceNode };
624 }
625
626 #Each bus gets its own hash entry in the array
627 for my $b (sort keys %busNodes) {
628 push @nodes, { %{$busNodes{$b}} };
629 }
630
631 return @nodes;
632}
633
634
635#Returns a hash of property names and values that should be stored in
636#the device tree node for this device. The names of the properties and
637#the attributes to find their values in are stored in the
638#BMC_DT_ATTR_NAMES attribute in the chip.
639# $chip = the chip target
640sub getPartDefinedDTProperties()
641{
642 my $chip = shift;
643 my %props;
644
645 if ($g_targetObj->isBadAttribute($chip, "BMC_DT_ATTR_NAMES")) {
646 return %props;
647 }
648
649 my $attr = $g_targetObj->getAttribute($chip, "BMC_DT_ATTR_NAMES");
650 $attr =~ s/\s//g;
651 my @names = split(',', $attr);
652
653 #There can be up to 4 entries in this attribute
654 for (my $i = 0; $i < scalar @names; $i += 2) {
655
656 #$names[$i] holds the name of the attribute.
657 #$names[$i+1] holds the name of the property to store its value in.
658 if (($names[$i] ne "NA") && ($names[$i] ne "")) {
659
660 my $val = $g_targetObj->getAttribute($chip, $names[$i]);
661
662 #if the value is empty, assume it's for a standalone property,
663 #which gets turned into: some-property;
664 if ($val eq "") {
665 $props{$names[$i+1]} = ZERO_LENGTH_PROPERTY;
666 }
667 else {
668 $props{$names[$i+1]} = "<$val>";
669 }
670 }
671 }
672
673 return %props;
674}
675
676
677#Convert the MRW I2C address into the format the dts needs
678# $addr = the I2C Address
679sub adjustI2CAddress()
680{
681 my $addr = shift;
682
683 #MRW holds the 8 bit value. We need the 7 bit one.
684 my $addr = $addr >> 1;
685 $addr = sprintf("0x%X", $addr);
686 $addr = lc $addr;
687
688 return $addr;
689}
690
691
692#Converts the I2C Bus number from the one in the MRW/schematics to the
693#kernel based number. Usually involves subtracting 1.
694# $num = I2C Bus number from the MRW
695sub adjustI2CBusNum()
696{
697 my $num = shift;
698
699 if (exists $I2C_BUS_ADJUST{$g_bmcModel}) {
700 my $op = $I2C_BUS_ADJUST{$g_bmcModel};
701 $num = $num + $op;
702 }
703 else {
704 print "WARNING: No I2C Bus number adjustment done " .
705 "for this BMC model\n";
706 }
707
708 return $num;
709}
710
Matt Spinler7d381e12016-09-27 14:27:24 -0500711
712#Returns a hash{'reg'} = "<.....>" based on the
713#BMC_DT_MEMORY attribute. This is used to display
714#memory ranges.
715sub getMemory()
716{
717 my $target = shift;
718 my $memory = $g_targetObj->getAttribute($target, "BMC_DT_MEMORY");
719 my @mem = split(',', $memory);
720 my %property;
Matt Spinler995f2a22016-09-30 13:07:31 -0500721 my $val = "< ";
Matt Spinler7d381e12016-09-27 14:27:24 -0500722
723 #Encoded as 4 <base address>,<size> pairs of memory ranges
724 #Unused ranges are all 0s.
725 #For now, assumes 32 bit numbers, revisit later for 64 bit support
726 #Convert it into: <num1 num2 num3 num4 etc>
727
728 for (my $i = 0;$i < scalar @mem;$i += 2) {
729
730 #pair is valid if size is nonzero
731 if (hex($mem[$i+1]) != 0) {
732 $val .= "$mem[$i] $mem[$i+1] ";
733 }
734 }
735
736 $val =~ s/\s$//;
Matt Spinler995f2a22016-09-30 13:07:31 -0500737 $val .= " >";
Matt Spinler7d381e12016-09-27 14:27:24 -0500738 $property{reg} = $val;
739
740 return %property;
741}
742
743
744#Returns a list of compatible fields for the BMC itself.
745sub getBMCCompatibles()
746{
747 my @compats;
748
Matt Spinler23d47c22016-10-04 12:31:21 -0500749 #1st entry: <system mfgr>,<system name>-bmc
750 #2nd entry: <bmc mfgr>,<bmc model>
Matt Spinler7d381e12016-09-27 14:27:24 -0500751
Matt Spinler23d47c22016-10-04 12:31:21 -0500752 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() }) {
753 if ($g_targetObj->getType($target) eq "SYS") {
754 my $mfgr = $g_targetObj->getAttribute($target, "MANUFACTURER");
755 push @compats, lc "$mfgr,$g_systemName-bmc";
756 last;
757 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500758 }
759
760 push @compats, lc($g_bmcMfgr).",".lc($g_bmcModel);
761
762 return @compats;
763}
764
765
766#Returns a string for the system's BMC model property
767sub getSystemBMCModel()
768{
Matt Spinler995f2a22016-09-30 13:07:31 -0500769 #'<System> BMC'
Matt Spinler7d381e12016-09-27 14:27:24 -0500770 my $sys = lc $g_systemName;
771 $sys = uc(substr($sys, 0, 1)) . substr($sys, 1);
772
773 return $sys . " BMC";
774}
775
776
777#Prints a list of nodes at the same indent level
778# $f = file handle
779# $level = indent level (0,1,etc)
780# @nodes = array of node hashes to print, where the
781# key for the hash is the name of the node
782sub printNodes()
783{
784 my ($f, $level, @nodes) = @_;
785
786 foreach my $n (@nodes) {
787 my %node = %$n;
788
789 foreach my $name (sort keys %node) {
790 my %n = %{ $node{$name} };
791 printNode($f, $level, $name, %n);
792 }
793 }
794}
795
796
797#Print a single node and its children
798# $f = file handle
799# $level = indent level (0,1,etc)
800# $name = the name of the node - shows up as:
801# name { ...
802# %vals = The contents of the node, with the following options:
803# if the key is:
804# - 'DTSI_INCLUDE', then value gets turned into a #include
Matt Spinler995f2a22016-09-30 13:07:31 -0500805# - 'COMMENT', then value gets turned into a // comment
Matt Spinler74909132016-10-07 13:52:19 -0500806# - 'ZERO_LENGTH_PROPERTY' then value gets turned into: value;
Matt Spinler7d381e12016-09-27 14:27:24 -0500807#
808# If the value is:
809# - a hash - then that hash gets turned into a child node
810# where the key is the name of the child node
Matt Spinler995f2a22016-09-30 13:07:31 -0500811# - an array of hashes indicates an array of child nodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500812sub printNode()
813{
814 my ($f, $level, $name, %vals) = @_;
815 my $include = "";
816
817 if ($level == 0) {
818 $name = "&".$name;
819 }
820
Matt Spinler995f2a22016-09-30 13:07:31 -0500821 print $f "\n";
822
823 if (exists $vals{COMMENT}) {
824 my @lines = split('\n', $vals{COMMENT});
825 foreach my $l (@lines) {
826 print $f indent($level) . "// $l\n";
827 }
828 }
829
830 print $f indent($level) . "$name {\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500831
Matt Spinler74909132016-10-07 13:52:19 -0500832 #First print properties, then includes, then subnodes
833
834 #Print Properties
Matt Spinler7d381e12016-09-27 14:27:24 -0500835 foreach my $v (sort keys %vals) {
836
Matt Spinler995f2a22016-09-30 13:07:31 -0500837 next if ($v eq "COMMENT");
Matt Spinler74909132016-10-07 13:52:19 -0500838 next if ($v eq "DTSI_INCLUDE");
839 next if (ref($vals{$v}) eq "HASH");
840 next if (ref($vals{$v}) eq "ARRAY");
Matt Spinler995f2a22016-09-30 13:07:31 -0500841
Matt Spinler74909132016-10-07 13:52:19 -0500842 if ($vals{$v} ne ZERO_LENGTH_PROPERTY) {
843 printProperty($f, $level+1, $v, $vals{$v});
Matt Spinler7d381e12016-09-27 14:27:24 -0500844 }
Matt Spinler74909132016-10-07 13:52:19 -0500845 else {
846 printZeroLengthProperty($f, $level+1, $v);
847 }
848 }
849
850 #Print Includes
851 foreach my $v (sort keys %vals) {
852
853 if ($v eq "DTSI_INCLUDE") {
854 #print 1 include per line
855 my @incs = split(',', $vals{$v});
856 foreach my $i (@incs) {
857 print $f qq(#include "$i";\n);
858 }
859 }
860 }
861
862 #Print Nodes
863 foreach my $v (sort keys %vals) {
864
865 if (ref($vals{$v}) eq "HASH") {
Matt Spinler7d381e12016-09-27 14:27:24 -0500866 printNode($f, $level+1, $v, %{$vals{$v}});
867 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500868 #An array of nested nodes
869 elsif (ref($vals{$v}) eq "ARRAY") {
870 my @array = @{$vals{$v}};
871 &printNodes($f, $level+1, @array);
872 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500873 }
874
875 print $f indent($level) . "};\n";
876}
877
878
879#Prints a comma separated list of properties.
880#e.g. a = "b, c, d";
881# $f = file handle
882# $level = indent level (0,1,etc)
883# $name = name of property
884# @vals = list of property values
885sub printPropertyList()
886{
887 my ($f, $level, $name, @vals) = @_;
888
889 print $f indent($level) . "$name = ";
890
891 for (my $i = 0;$i < scalar @vals; $i++) {
892 print $f "\"$vals[$i]\"";
893 if ($i < (scalar(@vals) - 1)) {
894 print $f ", ";
895 }
896 }
897 print $f ";\n"
898}
899
900
901#Prints a single property. e.g. a = "b";
902# $f = file handle
903# $level = indent level (0,1,etc)
904# $name = name of property
905# @vals = property values
906sub printProperty()
907{
908 my ($f, $level, $name, $val) = @_;
Matt Spinler23d47c22016-10-04 12:31:21 -0500909 my $quote = "\"";
910
911 $val = convertAlias($val);
912
913 #properties with < > or single word aliases don't need quotes
914 if (($val =~ /<.*>/) || ($val =~ /^&\w+$/)) {
915 $quote = "";
916 }
917
918 print $f indent($level) . "$name = $quote$val$quote;\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500919}
920
921
922#Prints a standalone property e.g. some-property;
923# $f = file handle
924# $level = indent level (0,1,etc)
925# $name = name of property
926sub printStandaloneProperty()
927{
928 my ($f, $level, $name) = @_;
929 print $f indent($level) . "$name;\n";
930}
931
932
933#Replace '(alias)' with '&'.
934#Needed because Serverwiz doesn't properly escape '&'s in the XML,
935#so the '(alias)' string is used to represent the alias
936#specifier instead of '&'.
937sub convertAlias() {
938 my $val = shift;
939 $val =~ s/\(alias\)/&/g;
940 return $val
941}
942
943
944#Returns the target for the BMC chip.
945#Not worrying about multiple BMC systems for now.
946sub getBMCTarget()
947{
948 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() })
949 {
950 if ($g_targetObj->getType($target) eq "BMC") {
951 return $target;
952 }
953 }
954 return "";
955}
956
957
958#Prints the device tree version line.
959# $f = file handle
960sub printVersion()
961{
962 my $f = shift;
963 print $f VERSION."\n"
964}
965
966
967#Prints the #include line for pulling in an include file.
968# $f = file handle
969# $level = indent level (0,1,etc)
970sub printIncludes()
971{
972 my ($f, $level) = @_;
973 my @includes = getIncludes($g_bmc);
974
975 foreach my $i (@includes) {
976 #if a .dtsi, gets " ", otherwise < >
977 if ($i =~ /\.dtsi$/) {
978 $i = "\"" . $i . "\"";
979 }
980 else {
981 $i = "<" . $i . ">";
982 }
983 print $f indent($level) . "#include $i;\n";
984 }
985}
986
987
988#Returns an array of includes from the BMC_DT_INCLUDES attribute
989#on the target passed in.
990# $target = the target to get the includes from
991sub getIncludes()
992{
993 my $target = shift;
994 my @includes;
995
Matt Spinler7d381e12016-09-27 14:27:24 -0500996 if (!$g_targetObj->isBadAttribute($target, "BMC_DT_INCLUDES")) {
997 my $attr = $g_targetObj->getAttribute($target, "BMC_DT_INCLUDES");
Matt Spinler995f2a22016-09-30 13:07:31 -0500998 $attr =~ s/\s+//g; #remove whitespace
999 $attr =~ s/NA,*//g; #remove the NAs
Matt Spinler7d381e12016-09-27 14:27:24 -05001000 my @incs = split(',', $attr);
1001
1002 foreach my $i (@incs) {
Matt Spinler995f2a22016-09-30 13:07:31 -05001003 push @includes, $i
Matt Spinler7d381e12016-09-27 14:27:24 -05001004 }
1005 }
1006
1007 return @includes;
1008}
1009
Matt Spinler74909132016-10-07 13:52:19 -05001010#Appends the first value of the 'reg' property
1011#passed in to the name passed in to create the
1012#full name for the node
1013# $name = node name that will be appended to
1014# $reg = the reg property values
1015sub makeNodeName()
1016{
1017 my ($name, $reg) = @_;
1018
1019 $reg =~ s/<//g;
1020 $reg =~ s/>//g;
1021 my @vals = split(' ', $reg);
1022
1023 if (scalar @vals > 0) {
1024 $vals[0] =~ s/0x//;
1025 $name .= "@" . lc $vals[0];
1026 }
1027
1028 return $name;
1029}
1030
Matt Spinler7d381e12016-09-27 14:27:24 -05001031
1032#Prints the root node starting bracket.
1033# $f = file handle
1034sub printRootNodeStart() {
1035 my $f = shift;
1036 print $f "\\ \{\n";
1037}
1038
1039
1040#Prints the root node ending bracket.
1041# $f = file handle
1042# $level = indent level (0,1,etc)
1043sub printRootNodeEnd() {
1044 my ($f, $level) = @_;
1045 print $f indent($level)."\};\n";
1046}
1047
1048
1049#Returns a string that can be used to indent based on the
1050#level passed in. Each level is an additional 4 spaces.
1051# $level = indent level (0,1,etc)
1052sub indent() {
1053 my $level = shift;
1054 return ' ' x ($level * 4);
1055}
1056
1057
Matt Spinler995f2a22016-09-30 13:07:31 -05001058#Will look for all the connections of the specified type coming from
1059#any sub target of the specified target, instead of just 1 level down
1060#like the Targets inteface does. Needed because sometimes we have
1061#target->pingroup->sourceunit instead of just target->sourceunit
1062# $target = the target to find connections off of
1063# $bus = the bus type
1064# $partType = destination part type, leave off if a don't care
1065sub findConnections() {
1066 my ($target, $bus, $partType) = @_;
1067 my %allConnections;
1068 my $i = 0;
1069
1070 #get the ones from target->child
1071 my $connections = $g_targetObj->findConnections($target, $bus, $partType);
1072 if ($connections ne "") {
1073 foreach my $c (@{$connections->{CONN}}) {
1074 $allConnections{CONN}[$i] = { %{$c} };
1075 $i++;
1076 }
1077 }
1078
1079 #get everything deeper
1080 my @children = getAllTargetChildren($target);
1081 foreach my $c (@children) {
1082 my $connections = $g_targetObj->findConnections($c, $bus, $partType);
1083 if ($connections ne "") {
1084
1085 foreach my $c (@{$connections->{CONN}}) {
1086 $allConnections{CONN}[$i] = { %{$c} };
1087 $i++;
1088 }
1089 }
1090 }
1091
Matt Spinler23d47c22016-10-04 12:31:21 -05001092 #Match the Targets::findConnections return strategy
1093 if (!keys %allConnections) {
1094 return "";
1095 }
1096
Matt Spinler995f2a22016-09-30 13:07:31 -05001097 return \%allConnections;
1098}
1099
1100#Returns every sub target, not just the 1st level children.
1101# $target = the target to find the children of
1102sub getAllTargetChildren()
1103{
1104 my $target = shift;
1105 my @children;
1106
1107 my $targets = $g_targetObj->getTargetChildren($target);
1108 if ($targets ne "") {
1109
1110 foreach my $t (@$targets) {
1111 push @children, $t;
1112 my @more = getAllTargetChildren($t);
1113 push @children, @more;
1114 }
1115 }
1116
1117 return @children;
1118}
1119
1120
Matt Spinler7d381e12016-09-27 14:27:24 -05001121sub printUsage
1122{
1123 print "gen_devtree.pl -x [XML filename] -o [output filename]\n";
1124 exit(1);
1125}