blob: 2853cf4b3c6c6daac76211f772ceeee239e08b8a [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
Matt Spinlerc0dff8a2016-11-02 15:47:30 -050064printNodes($f, 1, getOtherFlashNodes());
65
Matt Spinler995f2a22016-09-30 13:07:31 -050066printNode($f, 1, "leds", getLEDNode());
67
Matt Spinler30b461c2016-10-10 16:50:07 -050068printIncludes($f, ROOT_INCLUDES);
69
Matt Spinler7d381e12016-09-27 14:27:24 -050070printRootNodeEnd($f, 0);
71
Matt Spinler74909132016-10-07 13:52:19 -050072printNodes($f, 0, getI2CNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050073printNodes($f, 0, getMacNodes());
Matt Spinler995f2a22016-09-30 13:07:31 -050074printNodes($f, 0, getUARTNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050075printNodes($f, 0, getVuartNodes());
76
Matt Spinler30b461c2016-10-10 16:50:07 -050077printIncludes($f, POST_ROOT_INCLUDES);
78
Matt Spinler7d381e12016-09-27 14:27:24 -050079close $f;
80exit 0;
81
82
Matt Spinler74909132016-10-07 13:52:19 -050083#Finds the values for these globals:
84# $g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName
85sub setGlobalAttributes()
86{
87 $g_bmc = getBMCTarget();
88 if (length($g_bmc) == 0) {
89 die "Unable to find a BMC in this system\n";
90 }
91
92 if ($g_targetObj->isBadAttribute($g_bmc, "MODEL")) {
93 die "The MODEL attribute on $g_bmc is missing or empty.\n";
94 }
95 $g_bmcModel = $g_targetObj->getAttribute($g_bmc, "MODEL");
96
97 if ($g_targetObj->isBadAttribute($g_bmc, "MANUFACTURER")) {
98 die "The MANUFACTURER attribute on $g_bmc is missing or empty.\n";
99 }
100 $g_bmcMfgr = $g_targetObj->getAttribute($g_bmc, "MANUFACTURER");
101
102 $g_systemName = $g_targetObj->getSystemName();
103 if (length($g_systemName) == 0) {
104 die "The SYSTEM_NAME attribute is not set on the system target.\n";
105 }
106}
107
108
Matt Spinler23d47c22016-10-04 12:31:21 -0500109#Returns a hash that represents the 'aliases' node.
110#Will look like:
111# aliases {
112# name1 = &val1;
113# name2 = &val2;
114# ...
115# }
116sub getAliases()
117{
118 my %aliases;
Matt Spinler23d47c22016-10-04 12:31:21 -0500119
Matt Spinler30b461c2016-10-10 16:50:07 -0500120 #Get the info from the config file
Matt Spinler23d47c22016-10-04 12:31:21 -0500121
Matt Spinler30b461c2016-10-10 16:50:07 -0500122 if ((not exists $g_configuration{aliases}) ||
123 (keys %{$g_configuration{aliases}} == 0)) {
124 print "WARNING: Missing or empty 'aliases' section in config file.\n";
125 return %aliases;
126 }
127 %aliases = %{ $g_configuration{aliases} };
128
129 #add a & reference if one is missing
130 foreach my $a (keys %aliases) {
131 if (($aliases{$a} !~ /^&/) && ($aliases{$a} !~ /^\(ref\)/)) {
132 $aliases{$a} = "(ref)$aliases{$a}";
Matt Spinler23d47c22016-10-04 12:31:21 -0500133 }
134 }
135
136 return %aliases;
137}
138
Matt Spinler7d381e12016-09-27 14:27:24 -0500139
140#Return a hash that represents the 'chosen' node
Matt Spinler995f2a22016-09-30 13:07:31 -0500141#Will look like:
142# chosen {
143# stdout-path = ...
144# bootargs = ...
145# }
Matt Spinler7d381e12016-09-27 14:27:24 -0500146sub getChosen()
147{
Matt Spinler7d381e12016-09-27 14:27:24 -0500148 my %chosen;
Matt Spinler30b461c2016-10-10 16:50:07 -0500149 my @allowed = qw(bootargs stdin-path stdout-path);
150
151 #Get the info from the config file
152
153 if (not exists $g_configuration{chosen}) {
154 die "ERROR: Missing 'chosen' section in config file.\n";
155 }
156 %chosen = %{ $g_configuration{chosen} };
157
158 #Check for allowed entries. Empty is OK.
159 foreach my $key (keys %chosen) {
160 my $found = 0;
161 foreach my $good (@allowed) {
162 if ($key eq $good) {
163 $found = 1;
164 }
165 }
166
167 if ($found == 0) {
168 die "Invalid entry $key in 'chosen' section in config file\n";
169 }
170 }
171
Matt Spinler7d381e12016-09-27 14:27:24 -0500172 return %chosen;
173}
174
175
Matt Spinler30b461c2016-10-10 16:50:07 -0500176#Return a hash that represents the 'memory' node.
Matt Spinler995f2a22016-09-30 13:07:31 -0500177#Will look like:
Matt Spinler30b461c2016-10-10 16:50:07 -0500178# memory {
179# reg = < base size >
180# }
181sub getBmcMemory()
182{
183 my %memory;
184
185 #Get the info from the config file
186
187 if (not exists $g_configuration{memory}) {
188 die "ERROR: Missing 'memory' section in config file.\n";
189 }
190
191 if ((not exists $g_configuration{memory}{base}) ||
192 ($g_configuration{memory}{base} !~ /0x/)) {
193 die "ERROR: The base entry in the memory section in the config " .
194 "file is either missing or invalid.\n";
195 }
196
197 if ((not exists $g_configuration{memory}{size}) ||
198 ($g_configuration{memory}{size} !~ /0x/)) {
199 die "ERROR: The size entry in the memory section in the config " .
200 "file is either missing or invalid.\n";
201 }
202
203 #Future: could do more validation on the actual values
204
205 $memory{reg} = "<$g_configuration{memory}{base} " .
206 "$g_configuration{memory}{size}>";
207
208 return %memory;
209}
210
211
Matt Spinler25d60bb2016-10-31 15:16:03 -0500212#Returns an array of hashes representing the device tree nodes for
213#the BMC flash. These nodes are BMC model specific because different
214#models can have different device drivers.
215sub getBMCFlashNodes()
Matt Spinler7d381e12016-09-27 14:27:24 -0500216{
Matt Spinler25d60bb2016-10-31 15:16:03 -0500217 my @nodes;
218
219 if ($g_bmcModel eq "AST2500") {
220 my %node = getAST2500BMCSPIFlashNode();
221 push @nodes, { %node };
222 }
223 else {
224 die "ERROR: No BMC SPI flash support yet for BMC model $g_bmcModel\n";
225 }
226
227 return @nodes;
228}
229
230
231#Returns a hash that represents the BMC SPI flash(es) by finding the SPI
232#connections that come from the unit tagged as BMC_CODE. The code also
233#looks in the config file for any additional properties to add. Supports
234#the hardware where the same SPI master unit can be wired to more than 1
235#flash (a chip select line is used to switch between them.) This is
236#specific to the ASPEED AST2500 hardware and device driver.
237#Will look like:
238# fmc {
239# status = "okay"
240# flash@0 {
241# ...
242# };
243# flash@1 {
244# ...
245# };
246sub getAST2500BMCSPIFlashNode()
247{
248 my %bmcFlash;
249 my $chipSelect = 0;
250 my $lastUnit = "";
251
Matt Spinler18d5f572016-11-15 15:25:45 -0600252 my $connections = $g_targetObj->findConnections($g_bmc, "SPI", "FLASH");
Matt Spinler25d60bb2016-10-31 15:16:03 -0500253
254 if ($connections eq "") {
255 die "ERROR: No BMC SPI flashes found connected to the BMC\n";
256 }
257
258 $bmcFlash{fmc}{status} = "okay";
259
260 foreach my $spi (@{$connections->{CONN}}) {
261
262 #Looking for spi-masters with a function of 'BMC_CODE'.
263 #It's possible there are multiple flash chips here.
264 if (!$g_targetObj->isBadAttribute($spi->{SOURCE}, "SPI_FUNCTION")) {
265
266 my $function = $g_targetObj->getAttribute($spi->{SOURCE},
267 "SPI_FUNCTION");
268 if ($function eq "BMC_CODE") {
269
270 my $flashName = "flash@".$chipSelect;
271
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500272 $bmcFlash{fmc}{$flashName}{COMMENT} = connectionComment($spi);
Matt Spinler25d60bb2016-10-31 15:16:03 -0500273
274 $bmcFlash{fmc}{$flashName}{status} = "okay";
275
276 #Add in anything specified in the config file for this chip.
277 addBMCFlashConfigProperties(\%{$bmcFlash{fmc}{$flashName}},
278 $chipSelect);
279
280 #The code currently only supports the config where a chip
281 #select line is used to select between possibly multiple
282 #flash chips attached to the same SPI pins/unit. So we
283 #need to make sure if there are multiple chips found, that
284 #they are off of the same master unit.
285 if ($lastUnit eq "") {
286 $lastUnit = $spi->{SOURCE};
287 }
288 else {
289 if ($lastUnit ne $spi->{SOURCE}) {
290 die "ERROR: Currently only 1 spi-master unit is " .
291 "supported for BMC flash connections."
292 }
293 }
294
295 #Since we don't need anything chip select specific from the
296 #XML, we can just assign our own chip selects.
297 $chipSelect++;
298 }
299 }
300 }
301
302 if ($chipSelect == 0) {
303 die "ERROR: Didn't find any BMC flash chips connected";
304 }
305
306 return %bmcFlash;
307}
308
309
310#Looks in the bmc-flash-config section in the config file for the
311#chip select passed in to add any additional properties to the BMC
312#flash node.
313# $node = hash reference to the flash node
314# $cs = the flash chip select value
315sub addBMCFlashConfigProperties()
316{
317 my ($node, $cs) = @_;
318 my $section = "chip-select-$cs";
319
320 if (exists $g_configuration{"bmc-flash-config"}{$section}) {
321 foreach my $key (sort keys $g_configuration{"bmc-flash-config"}{$section}) {
322 $node->{$key} = $g_configuration{"bmc-flash-config"}{$section}{$key};
323 }
324 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500325}
326
327
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500328#Returns an array of hashes representing the other flashes used by the
329#BMC besides the ones that hold the BMC code. This is BMC model specific
330#as different models can have different interfaces.
331#Typically, these are SPI flashes.
332sub getOtherFlashNodes()
333{
334 my @nodes;
335
336 if ($g_bmcModel eq "AST2500") {
337 @nodes = getAST2500SpiFlashNodes();
338 }
339 else {
340 die "ERROR: No SPI flash support yet for BMC model $g_bmcModel\n";
341 }
342
343 return @nodes;
344}
345
346
347#Returns an array of hashes representing the SPI flashes in an
348#AST2500. These are for the SPI1 and SPI2 interfaces in the chip.
349#Each SPI master interface can support multiple flash chips. If
350#no hardware is connected to the interface, the node won't be present.
351sub getAST2500SpiFlashNodes()
352{
353 my @nodes;
354
355 #The AST2500 has 2 SPI master units, 1 and 2.
356 my @units = (1, 2);
357
358 foreach my $unit (@units) {
359
360 my %node = getAST2500SpiMasterNode($unit);
361
362 if (keys %node) {
363 my %spiNode;
364 my $nodeName = "spi$unit";
365 $spiNode{$nodeName} = { %node };
366 push @nodes, { %spiNode };
367 }
368 }
369
370 return @nodes;
371}
372
373
374#Returns a hash that represents the device tree node for the SPI1
375#or SPI2 master interface on the AST2500. Each master can support
376#multiple chips by use of a chip select.
377#Will look like:
378# spi1 {
379# status = "okay";
380# flash@0 {
381# ...
382# };
383# };
384#
385# $spiNum = The SPI master unit number to use
386sub getAST2500SpiMasterNode()
387{
388 my $spiNum = shift;
389 my %spiMaster;
390 my $chipSelect = 0;
391
Matt Spinler18d5f572016-11-15 15:25:45 -0600392 my $connections = $g_targetObj->findConnections($g_bmc, "SPI", "FLASH");
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500393
394 if ($connections eq "") {
395 return %spiMaster;
396 }
397
398 #Looking for spi-masters with a chip-unit of $spiNum
399 #It's possible there are multiple flash chips off the master
400 foreach my $spi (@{$connections->{CONN}}) {
401
402 my $unitNum = $g_targetObj->getAttribute($spi->{SOURCE},
403 "CHIP_UNIT");
404 if ($unitNum == $spiNum) {
405 $spiMaster{status} = "okay";
Matt Spinler2efdcba2016-11-08 15:37:20 -0600406
407 #Add in any pinctrl properties. These would come from the parent
408 #of $spi{SOURCE}, which would be a unit-pingroup-bmc if the
409 #pins for this connection are multi-function.
410 addPinCtrlProps($g_targetObj->getTargetParent($spi->{SOURCE}),
411 \%spiMaster);
412
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500413 my $flashName = "flash@".$chipSelect;
414
415 $spiMaster{$flashName}{COMMENT} = connectionComment($spi);
416
417 $spiMaster{$flashName}{status} = "okay";
418
419 $chipSelect++;
420 }
421 }
422
423 return %spiMaster;
424}
425
426
Matt Spinler995f2a22016-09-30 13:07:31 -0500427#Returns a hash that represents the leds node by finding all of the
428#GPIO connections to LEDs.
429#Node will look like:
430# leds {
431# <ledname> {
432# gpios = &gpio ASPEED_GPIO(x, y) GPIO_ACTIVE_xxx>
433# };
434# <another ledname> {
435# ...
436# }
437sub getLEDNode()
438{
439 my %leds;
440
Matt Spinlereca7f062016-11-07 09:59:23 -0600441 $leds{compatible} = "gpio-leds";
Matt Spinler995f2a22016-09-30 13:07:31 -0500442
Matt Spinler18d5f572016-11-15 15:25:45 -0600443 my $connections = $g_targetObj->findConnections($g_bmc, "GPIO", "LED");
Matt Spinler995f2a22016-09-30 13:07:31 -0500444
445 if ($connections eq "") {
446 print "WARNING: No LEDs found connected to the BMC\n";
447 return %leds;
448 }
449
450 foreach my $gpio (@{$connections->{CONN}}) {
451 my %ledNode;
452
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500453 $ledNode{COMMENT} = connectionComment($gpio);
Matt Spinler995f2a22016-09-30 13:07:31 -0500454
455 #The node name will be the simplified LED name
456 my $name = $gpio->{DEST_PARENT};
457 $name =~ s/(-\d+$)//; #remove trailing position
458 $name =~ s/.*\///; #remove the front of the path
459
460 #For now only supports ASPEED.
461 if (uc($g_bmcMfgr) ne "ASPEED") {
462 die "ERROR: Unsupported BMC manufacturer $g_bmcMfgr\n";
463 }
464 my $num = $g_targetObj->getAttribute($gpio->{SOURCE}, "PIN_NUM");
465 my $macro = getAspeedGpioMacro($num);
466
467 #If it's active high or low
468 my $state = $g_targetObj->getAttribute($gpio->{DEST_PARENT}, "ON_STATE");
469 my $activeString = getGpioActiveString($state);
470
471 $ledNode{gpios} = "<&gpio $macro $activeString>";
472
473 $leds{$name} = { %ledNode };
474 }
475
476 return %leds;
477}
478
479
480#Returns a either GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW
481# $val = either a 1 or a 0 for active high or low
482sub getGpioActiveString() {
483 my $val = shift;
484
485 if ($val == 0) {
486 return "GPIO_ACTIVE_LOW";
487 }
488
489 return "GPIO_ACTIVE_HIGH";
490}
491
492
493#Turns a GPIO number into something like ASPEED_GPIO(A, 0) for the
494#ASPEED GPIO numbering scheme A[0-7] -> Z[0-7] and then starts at
495#AA[0-7] after that.
496# $num = the GPIO number
497sub getAspeedGpioMacro() {
498 my $num = shift;
499 my $char;
500 my $offset = $num % 8;
501 my $block = int($num / 8);
502
503 #If past Z, wraps to AA, AB, etc
504 if ((ord('A') + $block) > ord('Z')) {
505 #how far past Z?
506 $char = $block - (ord('Z') - ord('A'));
507
508 #Don't let it wrap twice
509 if ($char > (ord('Z') - ord('A') + 1)) {
510 die "ERROR: Invalid PIN_NUM value $num found for GPIO\n";
511 }
512
513 #start back at 'A' again, and convert to a character
514 $char = chr($char + ord('A') - 1);
515
516 #Add in a bonus 'A', to get something like AB
517 $char = "A".$char;
518 }
519 else {
520 $char = ord('A') + $block;
521 $char = chr($char);
522 }
523
524 return "ASPEED_GPIO($char, $offset)";
525}
526
527
528#Returns a list of hashes that represent the UART nodes on the BMC by
529#finding the UART connections.
530#Nodes will look like:
531# &uartX {
532# status = "okay"
533# }
534sub getUARTNodes()
535{
536 my @nodes;
537
Matt Spinler23d47c22016-10-04 12:31:21 -0500538 #Using U750 for legacy MRW reasons
Matt Spinler18d5f572016-11-15 15:25:45 -0600539 my $connections = $g_targetObj->findConnections($g_bmc, "U750");
Matt Spinler995f2a22016-09-30 13:07:31 -0500540
541 if ($connections eq "") {
542 print "WARNING: No UART buses found connected to the BMC\n";
543 return @nodes;
544 }
545
546 foreach my $uart (@{$connections->{CONN}}) {
547 my %node;
548
549 my $num = $g_targetObj->getAttribute($uart->{SOURCE}, "CHIP_UNIT");
550 my $name = "uart$num";
551
552 $node{$name}{status} = "okay";
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500553 $node{$name}{COMMENT} = connectionComment($uart);
Matt Spinler995f2a22016-09-30 13:07:31 -0500554
Matt Spinler2efdcba2016-11-08 15:37:20 -0600555 #Add in any pinctrl properties. These would come from the parent
556 #of $uart{SOURCE}, which would be a unit-pingroup-bmc if the
557 #pins for this connection are multi-function.
558 addPinCtrlProps($g_targetObj->getTargetParent($uart->{SOURCE}),
559 \%{$node{$name}});
560
Matt Spinler995f2a22016-09-30 13:07:31 -0500561 push @nodes, { %node };
562 }
563
Matt Spinler7d381e12016-09-27 14:27:24 -0500564 return @nodes;
565}
566
567
Matt Spinler995f2a22016-09-30 13:07:31 -0500568#Returns a list of hashes that represent the MAC (ethernet) nodes on the BMC
569#by finding the connections of type ETHERNET.
570#Nodes will look like:
571# &macX {
572# ...
573# }
574sub getMacNodes()
575{
576 my @nodes;
577
Matt Spinler18d5f572016-11-15 15:25:45 -0600578 my $connections = $g_targetObj->findConnections($g_bmc, "ETHERNET");
Matt Spinler995f2a22016-09-30 13:07:31 -0500579
580 if ($connections eq "") {
581 print "WARNING: No ethernet buses found connected to the BMC\n";
582 return @nodes;
583 }
584
585 foreach my $eth (@{$connections->{CONN}}) {
586 my %node;
587
588 my $num = $g_targetObj->getAttribute($eth->{SOURCE}, "CHIP_UNIT");
589 my $ncsi = $g_targetObj->getAttribute($eth->{SOURCE}, "NCSI_MODE");
590 my $hwChecksum = $g_targetObj->getAttribute($eth->{SOURCE},
591 "USE_HW_CHECKSUM");
592
593 my $name = "mac$num";
594 $node{$name}{status} = "okay";
595
596 if ($ncsi == 1) {
Matt Spinler74909132016-10-07 13:52:19 -0500597 $node{$name}{"use-ncsi"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500598 }
599 if ($hwChecksum == 0) {
Matt Spinler74909132016-10-07 13:52:19 -0500600 $node{$name}{"no-hw-checksum"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500601 }
602
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500603 $node{$name}{COMMENT} = connectionComment($eth);
Matt Spinler995f2a22016-09-30 13:07:31 -0500604
Matt Spinler2efdcba2016-11-08 15:37:20 -0600605 #Add in any pinctrl properties. These would come from the parent
606 #of $eth{SOURCE}, which would be a unit-pingroup-bmc if the
607 #pins for this connection are multi-function.
608 addPinCtrlProps($g_targetObj->getTargetParent($eth->{SOURCE}),
609 \%{$node{$name}});
610
Matt Spinler995f2a22016-09-30 13:07:31 -0500611 push @nodes, { %node };
612 }
613
614 return @nodes;
615}
616
617
618#Returns a list of hashes that represent the virtual UART nodes
619#Node will look like:
620# &vuart {
621# status = "okay"
622# }
Matt Spinler7d381e12016-09-27 14:27:24 -0500623sub getVuartNodes()
624{
625 my @nodes;
626 my %node;
627
628 #For now, enable 1 node all the time.
Matt Spinler995f2a22016-09-30 13:07:31 -0500629 #TBD if this needs to be fixed
Matt Spinler7d381e12016-09-27 14:27:24 -0500630 $node{vuart}{status} = "okay";
631
632 push @nodes, { %node };
633
634 return @nodes;
635}
636
Matt Spinler74909132016-10-07 13:52:19 -0500637#Returns a list of hashes that represent the I2C device nodes.
638#There is 1 parent node for each bus, which then have subnodes
639#for each device on that bus. If a bus doesn't have any
640#attached devices, it doesn't need to show up.
641#The nodes will look like:
642# &i2c0 {
643# status = "okay"
644# device1@addr { (addr = 7 bit I2C address)
645# reg = <addr>
646# compatible = ...
647# ...
648# }
649# device2@addr {
650# reg = <addr>
651# ...
652# }
653# }
654# &i2c1 {
655# ...
656# }
657sub getI2CNodes()
658{
659 my @nodes;
660 my %busNodes;
661
Matt Spinler18d5f572016-11-15 15:25:45 -0600662 my $connections = $g_targetObj->findConnections($g_bmc, "I2C");
Matt Spinler74909132016-10-07 13:52:19 -0500663
664 if ($connections eq "") {
665 print "WARNING: No I2C buses found connected to the BMC\n";
666 return @nodes;
667 }
668
669 foreach my $i2c (@{$connections->{CONN}}) {
670
671 my %deviceNode, my $deviceName;
672
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500673 $deviceNode{COMMENT} = connectionComment($i2c);
Matt Spinler74909132016-10-07 13:52:19 -0500674
675 $deviceName = lc $i2c->{DEST_PARENT};
676 $deviceName =~ s/-\d+$//; #remove trailing position
677 $deviceName =~ s/.*\///; #remove the front of the path
678
679 #Get the I2C address
680 my $i2cAddress = $g_targetObj->getAttribute($i2c->{DEST}, "I2C_ADDRESS");
681 $i2cAddress = hex($i2cAddress);
682 if ($i2cAddress == 0) {
683 die "ERROR: Missing I2C address on $i2c->{DEST}\n";
684 }
685
686 #Put it in the format we want to print it in
687 $i2cAddress = adjustI2CAddress($i2cAddress);
688 $deviceNode{reg} = "<$i2cAddress>";
689
690 $deviceName = makeNodeName($deviceName, $deviceNode{reg});
691
692 #Get the I2C bus number
693 if ($g_targetObj->isBadAttribute($i2c->{SOURCE},
694 "I2C_PORT")) {
695 die "ERROR: I2C_PORT attribute in $i2c->{DEST_PARENT} " .
696 "is either missing or empty.\n";
697 }
698
699 my $busNum = $g_targetObj->getAttribute($i2c->{SOURCE}, "I2C_PORT");
700 if ($busNum =~ /0x/i) {
701 $busNum = hex($busNum);
702 }
703
704 #Convert the number to the Linux numbering scheme.
Matt Spinler30b461c2016-10-10 16:50:07 -0500705 $busNum += $g_i2cBusAdjust;
Matt Spinler74909132016-10-07 13:52:19 -0500706
707 #Get the compatible property
708 if ($g_targetObj->isBadAttribute($i2c->{DEST_PARENT},
709 "BMC_DT_COMPATIBLE")) {
710 die "ERROR: BMC_DT_COMPATIBLE attribute in $i2c->{DEST_PARENT} " .
711 "is either missing or empty.\n";
712 }
713
714 $deviceNode{compatible} = $g_targetObj->getAttribute(
715 $i2c->{DEST_PARENT},
716 "BMC_DT_COMPATIBLE");
717
718 #Get any other part specific properties, where the property
719 #names are actually defined in the XML.
720 my %props = getPartDefinedDTProperties($i2c->{DEST_PARENT});
721 foreach my $prop (sort keys %props) {
722 $deviceNode{$prop} = $props{$prop};
723 }
724
725 #busNodeName is the hash twice so when we loop
726 #below it doesn't get lost
727 my $busNodeName = "i2c$busNum";
728 $busNodes{$busNodeName}{$busNodeName}{status} = "okay";
729 $busNodes{$busNodeName}{$busNodeName}{$deviceName} = { %deviceNode };
Matt Spinler2efdcba2016-11-08 15:37:20 -0600730
731 #Add in any pinctrl properties. These would come from the parent
732 #of $i2c{SOURCE}, which would be a unit-pingroup-bmc if the
733 #pins for this connection are multi-function.
734 addPinCtrlProps($g_targetObj->getTargetParent($i2c->{SOURCE}),
735 \%{$busNodes{$busNodeName}{$busNodeName}});
Matt Spinler74909132016-10-07 13:52:19 -0500736 }
737
738 #Each bus gets its own hash entry in the array
739 for my $b (sort keys %busNodes) {
740 push @nodes, { %{$busNodes{$b}} };
741 }
742
743 return @nodes;
744}
745
746
747#Returns a hash of property names and values that should be stored in
748#the device tree node for this device. The names of the properties and
749#the attributes to find their values in are stored in the
750#BMC_DT_ATTR_NAMES attribute in the chip.
751# $chip = the chip target
752sub getPartDefinedDTProperties()
753{
754 my $chip = shift;
755 my %props;
756
757 if ($g_targetObj->isBadAttribute($chip, "BMC_DT_ATTR_NAMES")) {
758 return %props;
759 }
760
761 my $attr = $g_targetObj->getAttribute($chip, "BMC_DT_ATTR_NAMES");
762 $attr =~ s/\s//g;
763 my @names = split(',', $attr);
764
765 #There can be up to 4 entries in this attribute
766 for (my $i = 0; $i < scalar @names; $i += 2) {
767
768 #$names[$i] holds the name of the attribute.
769 #$names[$i+1] holds the name of the property to store its value in.
770 if (($names[$i] ne "NA") && ($names[$i] ne "")) {
771
772 my $val = $g_targetObj->getAttribute($chip, $names[$i]);
773
774 #if the value is empty, assume it's for a standalone property,
775 #which gets turned into: some-property;
776 if ($val eq "") {
777 $props{$names[$i+1]} = ZERO_LENGTH_PROPERTY;
778 }
779 else {
780 $props{$names[$i+1]} = "<$val>";
781 }
782 }
783 }
784
785 return %props;
786}
787
788
789#Convert the MRW I2C address into the format the dts needs
790# $addr = the I2C Address
791sub adjustI2CAddress()
792{
793 my $addr = shift;
794
795 #MRW holds the 8 bit value. We need the 7 bit one.
796 my $addr = $addr >> 1;
797 $addr = sprintf("0x%X", $addr);
798 $addr = lc $addr;
799
800 return $addr;
801}
802
803
Matt Spinler30b461c2016-10-10 16:50:07 -0500804#Sets the global $g_i2cBusAdjust from the configuration file.
805sub getI2CBusAdjust()
Matt Spinler74909132016-10-07 13:52:19 -0500806{
Matt Spinler30b461c2016-10-10 16:50:07 -0500807 if (exists $g_configuration{"i2c-bus-adjust"}) {
Matt Spinler74909132016-10-07 13:52:19 -0500808
Matt Spinler30b461c2016-10-10 16:50:07 -0500809 $g_i2cBusAdjust = $g_configuration{"i2c-bus-adjust"};
Matt Spinler74909132016-10-07 13:52:19 -0500810
Matt Spinler30b461c2016-10-10 16:50:07 -0500811 if (!looks_like_number($g_i2cBusAdjust)) {
812 die "ERROR: Invalid i2c-bus-adjust value $g_i2cBusAdjust " .
813 "found in config file.\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500814 }
815 }
Matt Spinler30b461c2016-10-10 16:50:07 -0500816 else {
817 $g_i2cBusAdjust = 0;
818 print "WARNING: No I2C Bus number adjustment done " .
819 "for this system.\n";
820 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500821}
822
823
Matt Spinler2efdcba2016-11-08 15:37:20 -0600824
825#Adds two pinctrl properties to the device node hash passed in,
826#if specified in the MRW. Pin Control refers to a mechanism for
827#Linux to know which function of a multi-function pin to configure.
828#For example, a pin could either be configured to be a GPIO, or
829#an I2C clock line. The pin function depends on board wiring,
830#so is known by the MRW.
831# $target = the target to get the BMC_DT_PINCTRL_FUNCTS attribute from
832# $node = a hash reference to the device tree node to add the properties to
833sub addPinCtrlProps()
834{
835 my ($target, $node) = @_;
836
837 if (!$g_targetObj->isBadAttribute($target, "BMC_DT_PINCTRL_FUNCS")) {
838 my $attr = $g_targetObj->getAttribute($target,
839 "BMC_DT_PINCTRL_FUNCS");
840
841 my $pinCtrl0Prop = makePinCtrl0PropValue($attr);
842 if ($pinCtrl0Prop ne "") {
843 $node->{"pinctrl-names"} = "default";
844 $node->{"pinctrl-0"} = $pinCtrl0Prop;
845 }
846 }
847}
848
849
850#Constructs the pinctrl-0 property value based on the
851#BMC_DT_PINCTRL_FUNCS attribute passed in.
852# $attr = BMC_DT_PINCTRL_FUNCS attribute value, which is an array
853sub makePinCtrl0PropValue()
854{
855 my $attr = shift;
856 my @entries;
857 my $value = "";
858
859 $attr =~ s/\s//g;
860 my @funcs = split(',', $attr);
861 foreach my $func (@funcs) {
862 if (($func ne "NA") && ($func ne "")) {
863 push @entries, $func;
864 }
865 }
866
867 #<&pinctrl_funcA_default &pinctrl_funcB_default ...>
868 if (scalar @entries) {
869 $value = "<";
870 foreach my $entry (@entries) {
871 $value .= "&pinctrl_".$entry."_default ";
872 }
873 $value =~ s/\s$//; #Remove the trailing space
874 $value .= ">";
875 }
876
877 return $value;
878}
879
880
Matt Spinler7d381e12016-09-27 14:27:24 -0500881#Returns a list of compatible fields for the BMC itself.
882sub getBMCCompatibles()
883{
884 my @compats;
885
Matt Spinler23d47c22016-10-04 12:31:21 -0500886 #1st entry: <system mfgr>,<system name>-bmc
887 #2nd entry: <bmc mfgr>,<bmc model>
Matt Spinler7d381e12016-09-27 14:27:24 -0500888
Matt Spinler23d47c22016-10-04 12:31:21 -0500889 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() }) {
890 if ($g_targetObj->getType($target) eq "SYS") {
891 my $mfgr = $g_targetObj->getAttribute($target, "MANUFACTURER");
892 push @compats, lc "$mfgr,$g_systemName-bmc";
893 last;
894 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500895 }
896
897 push @compats, lc($g_bmcMfgr).",".lc($g_bmcModel);
898
899 return @compats;
900}
901
902
903#Returns a string for the system's BMC model property
904sub getSystemBMCModel()
905{
Matt Spinler995f2a22016-09-30 13:07:31 -0500906 #'<System> BMC'
Matt Spinler7d381e12016-09-27 14:27:24 -0500907 my $sys = lc $g_systemName;
908 $sys = uc(substr($sys, 0, 1)) . substr($sys, 1);
909
910 return $sys . " BMC";
911}
912
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500913#Create the comment that will show up in the device tree
914#for a connection. In the output, will look like:
915# // sourceUnit ->
916# // destChip
917#
918# $conn = The connection hash reference
919sub connectionComment()
920{
921 my $conn = shift;
922 my $comment = "$conn->{SOURCE} ->\n$conn->{DEST_PARENT}";
923 return $comment;
924}
925
Matt Spinler7d381e12016-09-27 14:27:24 -0500926
927#Prints a list of nodes at the same indent level
928# $f = file handle
929# $level = indent level (0,1,etc)
930# @nodes = array of node hashes to print, where the
931# key for the hash is the name of the node
932sub printNodes()
933{
934 my ($f, $level, @nodes) = @_;
935
936 foreach my $n (@nodes) {
937 my %node = %$n;
938
939 foreach my $name (sort keys %node) {
940 my %n = %{ $node{$name} };
941 printNode($f, $level, $name, %n);
942 }
943 }
944}
945
946
947#Print a single node and its children
948# $f = file handle
949# $level = indent level (0,1,etc)
950# $name = the name of the node - shows up as:
951# name { ...
952# %vals = The contents of the node, with the following options:
953# if the key is:
954# - 'DTSI_INCLUDE', then value gets turned into a #include
Matt Spinler995f2a22016-09-30 13:07:31 -0500955# - 'COMMENT', then value gets turned into a // comment
Matt Spinler74909132016-10-07 13:52:19 -0500956# - 'ZERO_LENGTH_PROPERTY' then value gets turned into: value;
Matt Spinler7d381e12016-09-27 14:27:24 -0500957#
958# If the value is:
959# - a hash - then that hash gets turned into a child node
960# where the key is the name of the child node
Matt Spinler995f2a22016-09-30 13:07:31 -0500961# - an array of hashes indicates an array of child nodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500962sub printNode()
963{
964 my ($f, $level, $name, %vals) = @_;
965 my $include = "";
966
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500967 #No reason to print an empty node
968 if (!keys %vals) {
969 return;
970 }
971
Matt Spinler7d381e12016-09-27 14:27:24 -0500972 if ($level == 0) {
973 $name = "&".$name;
974 }
975
Matt Spinler995f2a22016-09-30 13:07:31 -0500976 print $f "\n";
977
978 if (exists $vals{COMMENT}) {
979 my @lines = split('\n', $vals{COMMENT});
980 foreach my $l (@lines) {
981 print $f indent($level) . "// $l\n";
982 }
983 }
984
985 print $f indent($level) . "$name {\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500986
Matt Spinler74909132016-10-07 13:52:19 -0500987 #First print properties, then includes, then subnodes
988
989 #Print Properties
Matt Spinler7d381e12016-09-27 14:27:24 -0500990 foreach my $v (sort keys %vals) {
991
Matt Spinler995f2a22016-09-30 13:07:31 -0500992 next if ($v eq "COMMENT");
Matt Spinler74909132016-10-07 13:52:19 -0500993 next if ($v eq "DTSI_INCLUDE");
994 next if (ref($vals{$v}) eq "HASH");
995 next if (ref($vals{$v}) eq "ARRAY");
Matt Spinler995f2a22016-09-30 13:07:31 -0500996
Matt Spinler74909132016-10-07 13:52:19 -0500997 if ($vals{$v} ne ZERO_LENGTH_PROPERTY) {
998 printProperty($f, $level+1, $v, $vals{$v});
Matt Spinler7d381e12016-09-27 14:27:24 -0500999 }
Matt Spinler74909132016-10-07 13:52:19 -05001000 else {
1001 printZeroLengthProperty($f, $level+1, $v);
1002 }
1003 }
1004
1005 #Print Includes
1006 foreach my $v (sort keys %vals) {
1007
1008 if ($v eq "DTSI_INCLUDE") {
1009 #print 1 include per line
1010 my @incs = split(',', $vals{$v});
1011 foreach my $i (@incs) {
1012 print $f qq(#include "$i";\n);
1013 }
1014 }
1015 }
1016
1017 #Print Nodes
1018 foreach my $v (sort keys %vals) {
1019
1020 if (ref($vals{$v}) eq "HASH") {
Matt Spinler7d381e12016-09-27 14:27:24 -05001021 printNode($f, $level+1, $v, %{$vals{$v}});
1022 }
Matt Spinler995f2a22016-09-30 13:07:31 -05001023 #An array of nested nodes
1024 elsif (ref($vals{$v}) eq "ARRAY") {
1025 my @array = @{$vals{$v}};
1026 &printNodes($f, $level+1, @array);
1027 }
Matt Spinler7d381e12016-09-27 14:27:24 -05001028 }
1029
1030 print $f indent($level) . "};\n";
1031}
1032
1033
1034#Prints a comma separated list of properties.
1035#e.g. a = "b, c, d";
1036# $f = file handle
1037# $level = indent level (0,1,etc)
1038# $name = name of property
1039# @vals = list of property values
1040sub printPropertyList()
1041{
1042 my ($f, $level, $name, @vals) = @_;
1043
1044 print $f indent($level) . "$name = ";
1045
1046 for (my $i = 0;$i < scalar @vals; $i++) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001047 print $f qq("$vals[$i]");
Matt Spinler7d381e12016-09-27 14:27:24 -05001048 if ($i < (scalar(@vals) - 1)) {
1049 print $f ", ";
1050 }
1051 }
1052 print $f ";\n"
1053}
1054
1055
1056#Prints a single property. e.g. a = "b";
1057# $f = file handle
1058# $level = indent level (0,1,etc)
1059# $name = name of property
1060# @vals = property values
1061sub printProperty()
1062{
1063 my ($f, $level, $name, $val) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001064 my $quoteChar = qq(");
Matt Spinler23d47c22016-10-04 12:31:21 -05001065
Matt Spinler30b461c2016-10-10 16:50:07 -05001066 $val = convertReference($val);
Matt Spinler23d47c22016-10-04 12:31:21 -05001067
1068 #properties with < > or single word aliases don't need quotes
1069 if (($val =~ /<.*>/) || ($val =~ /^&\w+$/)) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001070 $quoteChar = "";
Matt Spinler23d47c22016-10-04 12:31:21 -05001071 }
1072
Matt Spinler30b461c2016-10-10 16:50:07 -05001073 print $f indent($level) . "$name = $quoteChar$val$quoteChar;\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001074}
1075
1076
Matt Spinler30b461c2016-10-10 16:50:07 -05001077#Prints a zero length property e.g. some-property;
Matt Spinler7d381e12016-09-27 14:27:24 -05001078# $f = file handle
1079# $level = indent level (0,1,etc)
1080# $name = name of property
Matt Spinler30b461c2016-10-10 16:50:07 -05001081sub printZeroLengthProperty()
Matt Spinler7d381e12016-09-27 14:27:24 -05001082{
1083 my ($f, $level, $name) = @_;
1084 print $f indent($level) . "$name;\n";
1085}
1086
1087
Matt Spinler30b461c2016-10-10 16:50:07 -05001088#Replace '(ref)' with '&'.
Matt Spinler7d381e12016-09-27 14:27:24 -05001089#Needed because Serverwiz doesn't properly escape '&'s in the XML,
Matt Spinler30b461c2016-10-10 16:50:07 -05001090#so the '(ref)' string is used to represent the reference
Matt Spinler7d381e12016-09-27 14:27:24 -05001091#specifier instead of '&'.
Matt Spinler30b461c2016-10-10 16:50:07 -05001092sub convertReference() {
Matt Spinler7d381e12016-09-27 14:27:24 -05001093 my $val = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -05001094 $val =~ s/\(ref\)/&/g;
Matt Spinler7d381e12016-09-27 14:27:24 -05001095 return $val
1096}
1097
1098
1099#Returns the target for the BMC chip.
1100#Not worrying about multiple BMC systems for now.
1101sub getBMCTarget()
1102{
1103 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() })
1104 {
1105 if ($g_targetObj->getType($target) eq "BMC") {
1106 return $target;
1107 }
1108 }
1109 return "";
1110}
1111
1112
1113#Prints the device tree version line.
1114# $f = file handle
1115sub printVersion()
1116{
1117 my $f = shift;
1118 print $f VERSION."\n"
1119}
1120
1121
1122#Prints the #include line for pulling in an include file.
Matt Spinler30b461c2016-10-10 16:50:07 -05001123#The files to include come from the configuration file.
Matt Spinler7d381e12016-09-27 14:27:24 -05001124# $f = file handle
Matt Spinler30b461c2016-10-10 16:50:07 -05001125# $type = include type
Matt Spinler7d381e12016-09-27 14:27:24 -05001126sub printIncludes()
1127{
Matt Spinler30b461c2016-10-10 16:50:07 -05001128 my ($f, $type) = @_;
1129 my @includes = getIncludes($type);
Matt Spinler7d381e12016-09-27 14:27:24 -05001130
1131 foreach my $i (@includes) {
1132 #if a .dtsi, gets " ", otherwise < >
1133 if ($i =~ /\.dtsi$/) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001134 $i = qq("$i");
Matt Spinler7d381e12016-09-27 14:27:24 -05001135 }
1136 else {
Matt Spinler30b461c2016-10-10 16:50:07 -05001137 $i = "<$i>";
Matt Spinler7d381e12016-09-27 14:27:24 -05001138 }
Matt Spinler30b461c2016-10-10 16:50:07 -05001139 print $f "#include $i\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001140 }
1141}
1142
1143
Matt Spinler30b461c2016-10-10 16:50:07 -05001144#Returns an array of include files found in the config file
1145#for the type specified.
1146# $type = the include type, which is the section name in the
1147# YAML configuration file.
Matt Spinler7d381e12016-09-27 14:27:24 -05001148sub getIncludes()
1149{
Matt Spinler30b461c2016-10-10 16:50:07 -05001150 my $type = shift;
Matt Spinler7d381e12016-09-27 14:27:24 -05001151 my @includes;
1152
Matt Spinler30b461c2016-10-10 16:50:07 -05001153 #The config file may have a section but no includes
1154 #listed in it, which is OK.
1155 if ((exists $g_configuration{includes}{$type}) &&
1156 (ref($g_configuration{includes}{$type}) eq "ARRAY")) {
Matt Spinler7d381e12016-09-27 14:27:24 -05001157
Matt Spinler30b461c2016-10-10 16:50:07 -05001158 @includes = @{$g_configuration{includes}{$type}};
Matt Spinler7d381e12016-09-27 14:27:24 -05001159 }
1160
1161 return @includes;
1162}
1163
Matt Spinler30b461c2016-10-10 16:50:07 -05001164
Matt Spinler74909132016-10-07 13:52:19 -05001165#Appends the first value of the 'reg' property
1166#passed in to the name passed in to create the
1167#full name for the node
1168# $name = node name that will be appended to
1169# $reg = the reg property values
1170sub makeNodeName()
1171{
1172 my ($name, $reg) = @_;
1173
1174 $reg =~ s/<//g;
1175 $reg =~ s/>//g;
1176 my @vals = split(' ', $reg);
1177
1178 if (scalar @vals > 0) {
1179 $vals[0] =~ s/0x//;
1180 $name .= "@" . lc $vals[0];
1181 }
1182
1183 return $name;
1184}
1185
Matt Spinler7d381e12016-09-27 14:27:24 -05001186
1187#Prints the root node starting bracket.
1188# $f = file handle
1189sub printRootNodeStart() {
1190 my $f = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -05001191 print $f qq(/ {\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001192}
1193
1194
1195#Prints the root node ending bracket.
1196# $f = file handle
1197# $level = indent level (0,1,etc)
1198sub printRootNodeEnd() {
1199 my ($f, $level) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001200 print $f indent($level).qq(};\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001201}
1202
1203
1204#Returns a string that can be used to indent based on the
1205#level passed in. Each level is an additional 4 spaces.
1206# $level = indent level (0,1,etc)
1207sub indent() {
1208 my $level = shift;
1209 return ' ' x ($level * 4);
1210}
1211
1212
Matt Spinler7d381e12016-09-27 14:27:24 -05001213sub printUsage
1214{
Matt Spinler30b461c2016-10-10 16:50:07 -05001215 print "gen_devtree.pl -x [XML filename] -y [yaml config file] " .
1216 "-o [output filename]\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001217 exit(1);
1218}