blob: bb7c9b7c36a4d7a37c165e2c295323fcd07325b5 [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;
Matt Spinler889343f2017-01-30 14:14:11 -06007use warnings;
Matt Spinler7d381e12016-09-27 14:27:24 -05008use XML::Simple;
9use mrw::Targets;
Matt Spinlere2bf4392017-01-30 13:16:28 -060010use mrw::Util;
Matt Spinler7d381e12016-09-27 14:27:24 -050011use Getopt::Long;
Matt Spinler30b461c2016-10-10 16:50:07 -050012use YAML::Tiny qw(LoadFile);
13use Scalar::Util qw(looks_like_number);
Matt Spinler7d381e12016-09-27 14:27:24 -050014
Matt Spinler30b461c2016-10-10 16:50:07 -050015use constant {
16 VERSION => "/dts-v1/;",
17 ZERO_LENGTH_PROPERTY => "zero_length_property",
18 PRE_ROOT_INCLUDES => "pre-root-node",
19 ROOT_INCLUDES => "root-node",
Matt Spinler6d391252017-01-31 13:46:06 -060020 POST_ROOT_INCLUDES => "post-root-node",
21 HOST_SPI_FLASH_MEM_REGION_NODE_LABEL => "flash_memory"
Matt Spinler30b461c2016-10-10 16:50:07 -050022};
Matt Spinler74909132016-10-07 13:52:19 -050023
Matt Spinler7d381e12016-09-27 14:27:24 -050024
25my $serverwizFile;
Matt Spinler30b461c2016-10-10 16:50:07 -050026my $configFile;
Matt Spinler7d381e12016-09-27 14:27:24 -050027my $outputFile;
28my $debug;
29
30GetOptions("x=s" => \$serverwizFile,
Matt Spinler30b461c2016-10-10 16:50:07 -050031 "y=s" => \$configFile,
Matt Spinler7d381e12016-09-27 14:27:24 -050032 "o=s" => \$outputFile,
33 "d" => \$debug)
34or printUsage();
35
Matt Spinler30b461c2016-10-10 16:50:07 -050036if ((not defined $serverwizFile) || (not defined $outputFile) ||
37 (not defined $configFile)) {
Matt Spinler7d381e12016-09-27 14:27:24 -050038 printUsage();
39}
40
Matt Spinler30b461c2016-10-10 16:50:07 -050041my %g_configuration = %{ LoadFile($configFile) };
42
Matt Spinler7d381e12016-09-27 14:27:24 -050043my $g_targetObj = Targets->new;
44$g_targetObj->loadXML($serverwizFile);
45
Matt Spinler74909132016-10-07 13:52:19 -050046my ($g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName);
47setGlobalAttributes();
Matt Spinler7d381e12016-09-27 14:27:24 -050048
Matt Spinler30b461c2016-10-10 16:50:07 -050049my $g_i2cBusAdjust = 0;
50getI2CBusAdjust();
Matt Spinler7d381e12016-09-27 14:27:24 -050051
52open (my $f, ">$outputFile") or die "Could not open $outputFile\n";
53
54printVersion($f);
Matt Spinler30b461c2016-10-10 16:50:07 -050055printIncludes($f, PRE_ROOT_INCLUDES);
Matt Spinler7d381e12016-09-27 14:27:24 -050056printRootNodeStart($f);
57
58printPropertyList($f, 1, "model", getSystemBMCModel());
Matt Spinler7d381e12016-09-27 14:27:24 -050059printPropertyList($f, 1, "compatible", getBMCCompatibles());
Matt Spinler995f2a22016-09-30 13:07:31 -050060
Matt Spinler23d47c22016-10-04 12:31:21 -050061printNode($f, 1, "aliases", getAliases());
Matt Spinler7d381e12016-09-27 14:27:24 -050062printNode($f, 1, "chosen", getChosen());
Matt Spinler30b461c2016-10-10 16:50:07 -050063printNode($f, 1, "memory", getBmcMemory());
Matt Spinler6d391252017-01-31 13:46:06 -060064printNode($f, 1, "reserved-memory", getReservedMemory());
Matt Spinler7d381e12016-09-27 14:27:24 -050065
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 Spinler96f8f242016-11-28 16:26:57 -060072printNodes($f, 0, getBMCFlashNodes());
73
74printNodes($f, 0, getOtherFlashNodes());
75
Matt Spinler74909132016-10-07 13:52:19 -050076printNodes($f, 0, getI2CNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050077printNodes($f, 0, getMacNodes());
Matt Spinler995f2a22016-09-30 13:07:31 -050078printNodes($f, 0, getUARTNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050079printNodes($f, 0, getVuartNodes());
80
Matt Spinler30b461c2016-10-10 16:50:07 -050081printIncludes($f, POST_ROOT_INCLUDES);
82
Matt Spinler7d381e12016-09-27 14:27:24 -050083close $f;
84exit 0;
85
86
Matt Spinler74909132016-10-07 13:52:19 -050087#Finds the values for these globals:
88# $g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName
Matt Spinler889343f2017-01-30 14:14:11 -060089sub setGlobalAttributes
Matt Spinler74909132016-10-07 13:52:19 -050090{
Matt Spinlere2bf4392017-01-30 13:16:28 -060091 $g_bmc = Util::getBMCTarget($g_targetObj);
Matt Spinler74909132016-10-07 13:52:19 -050092
93 if ($g_targetObj->isBadAttribute($g_bmc, "MODEL")) {
94 die "The MODEL attribute on $g_bmc is missing or empty.\n";
95 }
96 $g_bmcModel = $g_targetObj->getAttribute($g_bmc, "MODEL");
97
98 if ($g_targetObj->isBadAttribute($g_bmc, "MANUFACTURER")) {
99 die "The MANUFACTURER attribute on $g_bmc is missing or empty.\n";
100 }
101 $g_bmcMfgr = $g_targetObj->getAttribute($g_bmc, "MANUFACTURER");
102
103 $g_systemName = $g_targetObj->getSystemName();
104 if (length($g_systemName) == 0) {
105 die "The SYSTEM_NAME attribute is not set on the system target.\n";
106 }
107}
108
109
Matt Spinler23d47c22016-10-04 12:31:21 -0500110#Returns a hash that represents the 'aliases' node.
111#Will look like:
112# aliases {
113# name1 = &val1;
114# name2 = &val2;
115# ...
116# }
Matt Spinler889343f2017-01-30 14:14:11 -0600117sub getAliases
Matt Spinler23d47c22016-10-04 12:31:21 -0500118{
119 my %aliases;
Matt Spinler23d47c22016-10-04 12:31:21 -0500120
Matt Spinler30b461c2016-10-10 16:50:07 -0500121 #Get the info from the config file
Matt Spinler23d47c22016-10-04 12:31:21 -0500122
Matt Spinler30b461c2016-10-10 16:50:07 -0500123 if ((not exists $g_configuration{aliases}) ||
124 (keys %{$g_configuration{aliases}} == 0)) {
125 print "WARNING: Missing or empty 'aliases' section in config file.\n";
126 return %aliases;
127 }
128 %aliases = %{ $g_configuration{aliases} };
129
130 #add a & reference if one is missing
131 foreach my $a (keys %aliases) {
132 if (($aliases{$a} !~ /^&/) && ($aliases{$a} !~ /^\(ref\)/)) {
133 $aliases{$a} = "(ref)$aliases{$a}";
Matt Spinler23d47c22016-10-04 12:31:21 -0500134 }
135 }
136
137 return %aliases;
138}
139
Matt Spinler7d381e12016-09-27 14:27:24 -0500140
141#Return a hash that represents the 'chosen' node
Matt Spinler995f2a22016-09-30 13:07:31 -0500142#Will look like:
143# chosen {
144# stdout-path = ...
145# bootargs = ...
146# }
Matt Spinler889343f2017-01-30 14:14:11 -0600147sub getChosen
Matt Spinler7d381e12016-09-27 14:27:24 -0500148{
Matt Spinler7d381e12016-09-27 14:27:24 -0500149 my %chosen;
Matt Spinler30b461c2016-10-10 16:50:07 -0500150 my @allowed = qw(bootargs stdin-path stdout-path);
151
152 #Get the info from the config file
153
154 if (not exists $g_configuration{chosen}) {
155 die "ERROR: Missing 'chosen' section in config file.\n";
156 }
157 %chosen = %{ $g_configuration{chosen} };
158
Matt Spinler30b461c2016-10-10 16:50:07 -0500159 foreach my $key (keys %chosen) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500160
Matt Spinler28fb1a92017-01-30 12:54:10 -0600161 #Check for allowed entries. Empty is OK.
162 if (!grep(/^$key$/, @allowed)) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500163 die "Invalid entry $key in 'chosen' section in config file\n";
164 }
Matt Spinler28fb1a92017-01-30 12:54:10 -0600165
166 #stdout-path and stdin-path can use aliases, which will look like
167 #(alias)uart5 in the yaml. Change to (ref)uart5 so it will be
168 #converted to a '&' later.
169 $chosen{$key} =~ s/\(alias\)/\(ref\)/g;
Matt Spinler30b461c2016-10-10 16:50:07 -0500170 }
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# }
Matt Spinler889343f2017-01-30 14:14:11 -0600181sub getBmcMemory
Matt Spinler30b461c2016-10-10 16:50:07 -0500182{
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 Spinler6d391252017-01-31 13:46:06 -0600212#Returns a hash that represents the 'reserved-memory' node.
213#This currently only supports the memory region for the LPC
214#host spi flash mailbox. Will look like:
215# reserved-memory {
216# #address-cells = <1>;
217# #size-cells = <1>;
218# ranges;
219#
220# flash_memory: region@94000000 {
221# no-map;
222# reg = <0x94000000 0x04000000>;
223# };
224# };
225sub getReservedMemory
226{
227 my %memory;
228
229 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
230 return %memory;
231 }
232
233 $memory{"#address-cells"} = "<1>";
234 $memory{"#size-cells"} = "<1>";
235 $memory{ranges} = ZERO_LENGTH_PROPERTY;
236
237 #Get the sub node that contains the address range
238 my ($name, $node) = getHostSpiFlashMboxRegion();
239 $memory{$name} = { %$node };
240
241 return %memory;
242}
243
244
245#Returns a hash that represents a child node of the
246#reserved-memory node which contains the address range
247#that the host spi flash is mapped to.
248sub getHostSpiFlashMboxRegion
249{
250 my %node;
251
252 $node{"no-map"} = ZERO_LENGTH_PROPERTY;
253
254 #This node needs a label the LPC node can refer to.
255 $node{NODE_LABEL} = HOST_SPI_FLASH_MEM_REGION_NODE_LABEL;
256
257 #Get the memory region's base address and size from the config file
258 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}
259 {"bmc-address-range"}{base}) {
260 die "Could not find lpc-host-spi-flash-mailbox base " .
261 "address in config file\n";
262 }
263
264 my $base = $g_configuration{"lpc-host-spi-flash-mailbox"}
265 {"bmc-address-range"}{base};
266 #Allow 1 hex value, up to 4B
267 if ($base !~ /^0x[0-9a-fA-F]{1,8}$/) {
268 die "lpc-host-spi-flash-mailbox base address $base is invalid\n";
269 }
270
271 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}
272 {"bmc-address-range"}{size}) {
273 die "Could not find lpc-host-spi-flash-mailbox address size " .
274 "in config file\n";
275 }
276
277 my $size = $g_configuration{"lpc-host-spi-flash-mailbox"}
278 {"bmc-address-range"}{size};
279 if ($size !~ /^0x[0-9a-fA-F]{1,8}$/) {
280 die "lpc-host-spi-flash-mailbox address range size " .
281 "$size is invalid\n";
282 }
283
284 $node{"reg"} = "<$base $size>";
285 my $name = makeNodeName("region", $node{reg});
286
287 return ($name, \%node);
288}
289
290
Matt Spinler25d60bb2016-10-31 15:16:03 -0500291#Returns an array of hashes representing the device tree nodes for
292#the BMC flash. These nodes are BMC model specific because different
293#models can have different device drivers.
Matt Spinler889343f2017-01-30 14:14:11 -0600294sub getBMCFlashNodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500295{
Matt Spinler25d60bb2016-10-31 15:16:03 -0500296 my @nodes;
297
298 if ($g_bmcModel eq "AST2500") {
299 my %node = getAST2500BMCSPIFlashNode();
300 push @nodes, { %node };
301 }
302 else {
303 die "ERROR: No BMC SPI flash support yet for BMC model $g_bmcModel\n";
304 }
305
306 return @nodes;
307}
308
309
310#Returns a hash that represents the BMC SPI flash(es) by finding the SPI
311#connections that come from the unit tagged as BMC_CODE. The code also
312#looks in the config file for any additional properties to add. Supports
313#the hardware where the same SPI master unit can be wired to more than 1
314#flash (a chip select line is used to switch between them.) This is
315#specific to the ASPEED AST2500 hardware and device driver.
316#Will look like:
317# fmc {
318# status = "okay"
319# flash@0 {
320# ...
321# };
322# flash@1 {
323# ...
324# };
Matt Spinler889343f2017-01-30 14:14:11 -0600325sub getAST2500BMCSPIFlashNode
Matt Spinler25d60bb2016-10-31 15:16:03 -0500326{
327 my %bmcFlash;
328 my $chipSelect = 0;
329 my $lastUnit = "";
330
Matt Spinler18d5f572016-11-15 15:25:45 -0600331 my $connections = $g_targetObj->findConnections($g_bmc, "SPI", "FLASH");
Matt Spinler25d60bb2016-10-31 15:16:03 -0500332
333 if ($connections eq "") {
334 die "ERROR: No BMC SPI flashes found connected to the BMC\n";
335 }
336
337 $bmcFlash{fmc}{status} = "okay";
338
339 foreach my $spi (@{$connections->{CONN}}) {
340
341 #Looking for spi-masters with a function of 'BMC_CODE'.
342 #It's possible there are multiple flash chips here.
343 if (!$g_targetObj->isBadAttribute($spi->{SOURCE}, "SPI_FUNCTION")) {
344
345 my $function = $g_targetObj->getAttribute($spi->{SOURCE},
346 "SPI_FUNCTION");
347 if ($function eq "BMC_CODE") {
348
349 my $flashName = "flash@".$chipSelect;
350
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500351 $bmcFlash{fmc}{$flashName}{COMMENT} = connectionComment($spi);
Matt Spinler25d60bb2016-10-31 15:16:03 -0500352
353 $bmcFlash{fmc}{$flashName}{status} = "okay";
354
355 #Add in anything specified in the config file for this chip.
356 addBMCFlashConfigProperties(\%{$bmcFlash{fmc}{$flashName}},
357 $chipSelect);
358
359 #The code currently only supports the config where a chip
360 #select line is used to select between possibly multiple
361 #flash chips attached to the same SPI pins/unit. So we
362 #need to make sure if there are multiple chips found, that
363 #they are off of the same master unit.
364 if ($lastUnit eq "") {
365 $lastUnit = $spi->{SOURCE};
366 }
367 else {
368 if ($lastUnit ne $spi->{SOURCE}) {
369 die "ERROR: Currently only 1 spi-master unit is " .
370 "supported for BMC flash connections."
371 }
372 }
373
374 #Since we don't need anything chip select specific from the
375 #XML, we can just assign our own chip selects.
376 $chipSelect++;
377 }
378 }
379 }
380
381 if ($chipSelect == 0) {
382 die "ERROR: Didn't find any BMC flash chips connected";
383 }
384
385 return %bmcFlash;
386}
387
388
389#Looks in the bmc-flash-config section in the config file for the
390#chip select passed in to add any additional properties to the BMC
391#flash node.
392# $node = hash reference to the flash node
393# $cs = the flash chip select value
Matt Spinler889343f2017-01-30 14:14:11 -0600394sub addBMCFlashConfigProperties
Matt Spinler25d60bb2016-10-31 15:16:03 -0500395{
396 my ($node, $cs) = @_;
397 my $section = "chip-select-$cs";
398
399 if (exists $g_configuration{"bmc-flash-config"}{$section}) {
400 foreach my $key (sort keys $g_configuration{"bmc-flash-config"}{$section}) {
401 $node->{$key} = $g_configuration{"bmc-flash-config"}{$section}{$key};
402 }
403 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500404}
405
406
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500407#Returns an array of hashes representing the other flashes used by the
408#BMC besides the ones that hold the BMC code. This is BMC model specific
409#as different models can have different interfaces.
410#Typically, these are SPI flashes.
Matt Spinler889343f2017-01-30 14:14:11 -0600411sub getOtherFlashNodes
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500412{
413 my @nodes;
414
415 if ($g_bmcModel eq "AST2500") {
416 @nodes = getAST2500SpiFlashNodes();
417 }
418 else {
419 die "ERROR: No SPI flash support yet for BMC model $g_bmcModel\n";
420 }
421
422 return @nodes;
423}
424
425
426#Returns an array of hashes representing the SPI flashes in an
427#AST2500. These are for the SPI1 and SPI2 interfaces in the chip.
428#Each SPI master interface can support multiple flash chips. If
429#no hardware is connected to the interface, the node won't be present.
Matt Spinler889343f2017-01-30 14:14:11 -0600430sub getAST2500SpiFlashNodes
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500431{
432 my @nodes;
433
434 #The AST2500 has 2 SPI master units, 1 and 2.
435 my @units = (1, 2);
436
437 foreach my $unit (@units) {
438
439 my %node = getAST2500SpiMasterNode($unit);
440
441 if (keys %node) {
442 my %spiNode;
443 my $nodeName = "spi$unit";
444 $spiNode{$nodeName} = { %node };
445 push @nodes, { %spiNode };
446 }
447 }
448
449 return @nodes;
450}
451
452
453#Returns a hash that represents the device tree node for the SPI1
454#or SPI2 master interface on the AST2500. Each master can support
455#multiple chips by use of a chip select.
456#Will look like:
457# spi1 {
458# status = "okay";
459# flash@0 {
460# ...
461# };
462# };
463#
464# $spiNum = The SPI master unit number to use
Matt Spinler889343f2017-01-30 14:14:11 -0600465sub getAST2500SpiMasterNode
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500466{
467 my $spiNum = shift;
468 my %spiMaster;
469 my $chipSelect = 0;
470
Matt Spinler18d5f572016-11-15 15:25:45 -0600471 my $connections = $g_targetObj->findConnections($g_bmc, "SPI", "FLASH");
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500472
473 if ($connections eq "") {
474 return %spiMaster;
475 }
476
477 #Looking for spi-masters with a chip-unit of $spiNum
478 #It's possible there are multiple flash chips off the master
479 foreach my $spi (@{$connections->{CONN}}) {
480
481 my $unitNum = $g_targetObj->getAttribute($spi->{SOURCE},
482 "CHIP_UNIT");
483 if ($unitNum == $spiNum) {
484 $spiMaster{status} = "okay";
Matt Spinler2efdcba2016-11-08 15:37:20 -0600485
486 #Add in any pinctrl properties. These would come from the parent
487 #of $spi{SOURCE}, which would be a unit-pingroup-bmc if the
488 #pins for this connection are multi-function.
489 addPinCtrlProps($g_targetObj->getTargetParent($spi->{SOURCE}),
490 \%spiMaster);
491
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500492 my $flashName = "flash@".$chipSelect;
493
494 $spiMaster{$flashName}{COMMENT} = connectionComment($spi);
495
496 $spiMaster{$flashName}{status} = "okay";
497
Matt Spinler0eda4882016-11-30 15:20:11 -0600498 #AST2500 PNORs need a label
499 my $function = $g_targetObj->getAttribute($spi->{SOURCE},
500 "SPI_FUNCTION");
501 if ($function eq "PNOR") {
502 $spiMaster{$flashName}{label} = "pnor";
503 }
504
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500505 $chipSelect++;
506 }
507 }
508
509 return %spiMaster;
510}
511
512
Matt Spinler995f2a22016-09-30 13:07:31 -0500513#Returns a hash that represents the leds node by finding all of the
514#GPIO connections to LEDs.
515#Node will look like:
516# leds {
517# <ledname> {
518# gpios = &gpio ASPEED_GPIO(x, y) GPIO_ACTIVE_xxx>
519# };
520# <another ledname> {
521# ...
522# }
Matt Spinler889343f2017-01-30 14:14:11 -0600523sub getLEDNode
Matt Spinler995f2a22016-09-30 13:07:31 -0500524{
525 my %leds;
526
Matt Spinlereca7f062016-11-07 09:59:23 -0600527 $leds{compatible} = "gpio-leds";
Matt Spinler995f2a22016-09-30 13:07:31 -0500528
Matt Spinler18d5f572016-11-15 15:25:45 -0600529 my $connections = $g_targetObj->findConnections($g_bmc, "GPIO", "LED");
Matt Spinler995f2a22016-09-30 13:07:31 -0500530
531 if ($connections eq "") {
532 print "WARNING: No LEDs found connected to the BMC\n";
533 return %leds;
534 }
535
536 foreach my $gpio (@{$connections->{CONN}}) {
537 my %ledNode;
538
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500539 $ledNode{COMMENT} = connectionComment($gpio);
Matt Spinler995f2a22016-09-30 13:07:31 -0500540
541 #The node name will be the simplified LED name
542 my $name = $gpio->{DEST_PARENT};
543 $name =~ s/(-\d+$)//; #remove trailing position
544 $name =~ s/.*\///; #remove the front of the path
545
546 #For now only supports ASPEED.
547 if (uc($g_bmcMfgr) ne "ASPEED") {
548 die "ERROR: Unsupported BMC manufacturer $g_bmcMfgr\n";
549 }
550 my $num = $g_targetObj->getAttribute($gpio->{SOURCE}, "PIN_NUM");
551 my $macro = getAspeedGpioMacro($num);
552
553 #If it's active high or low
554 my $state = $g_targetObj->getAttribute($gpio->{DEST_PARENT}, "ON_STATE");
555 my $activeString = getGpioActiveString($state);
556
557 $ledNode{gpios} = "<&gpio $macro $activeString>";
558
559 $leds{$name} = { %ledNode };
560 }
561
562 return %leds;
563}
564
565
566#Returns a either GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW
567# $val = either a 1 or a 0 for active high or low
Matt Spinler889343f2017-01-30 14:14:11 -0600568sub getGpioActiveString
569{
Matt Spinler995f2a22016-09-30 13:07:31 -0500570 my $val = shift;
571
572 if ($val == 0) {
573 return "GPIO_ACTIVE_LOW";
574 }
575
576 return "GPIO_ACTIVE_HIGH";
577}
578
579
580#Turns a GPIO number into something like ASPEED_GPIO(A, 0) for the
581#ASPEED GPIO numbering scheme A[0-7] -> Z[0-7] and then starts at
582#AA[0-7] after that.
583# $num = the GPIO number
Matt Spinler889343f2017-01-30 14:14:11 -0600584sub getAspeedGpioMacro
585{
Matt Spinler995f2a22016-09-30 13:07:31 -0500586 my $num = shift;
587 my $char;
588 my $offset = $num % 8;
589 my $block = int($num / 8);
590
591 #If past Z, wraps to AA, AB, etc
592 if ((ord('A') + $block) > ord('Z')) {
593 #how far past Z?
594 $char = $block - (ord('Z') - ord('A'));
595
596 #Don't let it wrap twice
597 if ($char > (ord('Z') - ord('A') + 1)) {
598 die "ERROR: Invalid PIN_NUM value $num found for GPIO\n";
599 }
600
601 #start back at 'A' again, and convert to a character
602 $char = chr($char + ord('A') - 1);
603
604 #Add in a bonus 'A', to get something like AB
605 $char = "A".$char;
606 }
607 else {
608 $char = ord('A') + $block;
609 $char = chr($char);
610 }
611
612 return "ASPEED_GPIO($char, $offset)";
613}
614
615
616#Returns a list of hashes that represent the UART nodes on the BMC by
617#finding the UART connections.
618#Nodes will look like:
619# &uartX {
620# status = "okay"
621# }
Matt Spinler889343f2017-01-30 14:14:11 -0600622sub getUARTNodes
Matt Spinler995f2a22016-09-30 13:07:31 -0500623{
624 my @nodes;
625
Matt Spinler23d47c22016-10-04 12:31:21 -0500626 #Using U750 for legacy MRW reasons
Matt Spinler18d5f572016-11-15 15:25:45 -0600627 my $connections = $g_targetObj->findConnections($g_bmc, "U750");
Matt Spinler995f2a22016-09-30 13:07:31 -0500628
629 if ($connections eq "") {
630 print "WARNING: No UART buses found connected to the BMC\n";
631 return @nodes;
632 }
633
634 foreach my $uart (@{$connections->{CONN}}) {
635 my %node;
636
637 my $num = $g_targetObj->getAttribute($uart->{SOURCE}, "CHIP_UNIT");
638 my $name = "uart$num";
639
640 $node{$name}{status} = "okay";
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500641 $node{$name}{COMMENT} = connectionComment($uart);
Matt Spinler995f2a22016-09-30 13:07:31 -0500642
Matt Spinler2efdcba2016-11-08 15:37:20 -0600643 #Add in any pinctrl properties. These would come from the parent
644 #of $uart{SOURCE}, which would be a unit-pingroup-bmc if the
645 #pins for this connection are multi-function.
646 addPinCtrlProps($g_targetObj->getTargetParent($uart->{SOURCE}),
647 \%{$node{$name}});
648
Matt Spinler995f2a22016-09-30 13:07:31 -0500649 push @nodes, { %node };
650 }
651
Matt Spinler7d381e12016-09-27 14:27:24 -0500652 return @nodes;
653}
654
655
Matt Spinler995f2a22016-09-30 13:07:31 -0500656#Returns a list of hashes that represent the MAC (ethernet) nodes on the BMC
657#by finding the connections of type ETHERNET.
658#Nodes will look like:
659# &macX {
660# ...
661# }
Matt Spinler889343f2017-01-30 14:14:11 -0600662sub getMacNodes
Matt Spinler995f2a22016-09-30 13:07:31 -0500663{
664 my @nodes;
665
Matt Spinler18d5f572016-11-15 15:25:45 -0600666 my $connections = $g_targetObj->findConnections($g_bmc, "ETHERNET");
Matt Spinler995f2a22016-09-30 13:07:31 -0500667
668 if ($connections eq "") {
669 print "WARNING: No ethernet buses found connected to the BMC\n";
670 return @nodes;
671 }
672
673 foreach my $eth (@{$connections->{CONN}}) {
674 my %node;
675
676 my $num = $g_targetObj->getAttribute($eth->{SOURCE}, "CHIP_UNIT");
677 my $ncsi = $g_targetObj->getAttribute($eth->{SOURCE}, "NCSI_MODE");
678 my $hwChecksum = $g_targetObj->getAttribute($eth->{SOURCE},
679 "USE_HW_CHECKSUM");
680
681 my $name = "mac$num";
682 $node{$name}{status} = "okay";
683
684 if ($ncsi == 1) {
Matt Spinler74909132016-10-07 13:52:19 -0500685 $node{$name}{"use-ncsi"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500686 }
687 if ($hwChecksum == 0) {
Matt Spinler74909132016-10-07 13:52:19 -0500688 $node{$name}{"no-hw-checksum"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500689 }
690
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500691 $node{$name}{COMMENT} = connectionComment($eth);
Matt Spinler995f2a22016-09-30 13:07:31 -0500692
Matt Spinler2efdcba2016-11-08 15:37:20 -0600693 #Add in any pinctrl properties. These would come from the parent
694 #of $eth{SOURCE}, which would be a unit-pingroup-bmc if the
695 #pins for this connection are multi-function.
696 addPinCtrlProps($g_targetObj->getTargetParent($eth->{SOURCE}),
697 \%{$node{$name}});
698
Matt Spinler995f2a22016-09-30 13:07:31 -0500699 push @nodes, { %node };
700 }
701
702 return @nodes;
703}
704
705
706#Returns a list of hashes that represent the virtual UART nodes
707#Node will look like:
708# &vuart {
709# status = "okay"
710# }
Matt Spinler889343f2017-01-30 14:14:11 -0600711sub getVuartNodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500712{
713 my @nodes;
714 my %node;
715
716 #For now, enable 1 node all the time.
Matt Spinler995f2a22016-09-30 13:07:31 -0500717 #TBD if this needs to be fixed
Matt Spinler7d381e12016-09-27 14:27:24 -0500718 $node{vuart}{status} = "okay";
719
720 push @nodes, { %node };
721
722 return @nodes;
723}
724
Matt Spinler74909132016-10-07 13:52:19 -0500725#Returns a list of hashes that represent the I2C device nodes.
726#There is 1 parent node for each bus, which then have subnodes
727#for each device on that bus. If a bus doesn't have any
728#attached devices, it doesn't need to show up.
729#The nodes will look like:
730# &i2c0 {
731# status = "okay"
732# device1@addr { (addr = 7 bit I2C address)
733# reg = <addr>
734# compatible = ...
735# ...
736# }
737# device2@addr {
738# reg = <addr>
739# ...
740# }
741# }
742# &i2c1 {
743# ...
744# }
Matt Spinler889343f2017-01-30 14:14:11 -0600745sub getI2CNodes
Matt Spinler74909132016-10-07 13:52:19 -0500746{
747 my @nodes;
748 my %busNodes;
749
Matt Spinler18d5f572016-11-15 15:25:45 -0600750 my $connections = $g_targetObj->findConnections($g_bmc, "I2C");
Matt Spinler74909132016-10-07 13:52:19 -0500751
752 if ($connections eq "") {
753 print "WARNING: No I2C buses found connected to the BMC\n";
754 return @nodes;
755 }
756
757 foreach my $i2c (@{$connections->{CONN}}) {
758
759 my %deviceNode, my $deviceName;
760
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500761 $deviceNode{COMMENT} = connectionComment($i2c);
Matt Spinler74909132016-10-07 13:52:19 -0500762
763 $deviceName = lc $i2c->{DEST_PARENT};
764 $deviceName =~ s/-\d+$//; #remove trailing position
765 $deviceName =~ s/.*\///; #remove the front of the path
766
767 #Get the I2C address
768 my $i2cAddress = $g_targetObj->getAttribute($i2c->{DEST}, "I2C_ADDRESS");
769 $i2cAddress = hex($i2cAddress);
770 if ($i2cAddress == 0) {
771 die "ERROR: Missing I2C address on $i2c->{DEST}\n";
772 }
773
774 #Put it in the format we want to print it in
775 $i2cAddress = adjustI2CAddress($i2cAddress);
776 $deviceNode{reg} = "<$i2cAddress>";
777
778 $deviceName = makeNodeName($deviceName, $deviceNode{reg});
779
780 #Get the I2C bus number
781 if ($g_targetObj->isBadAttribute($i2c->{SOURCE},
782 "I2C_PORT")) {
783 die "ERROR: I2C_PORT attribute in $i2c->{DEST_PARENT} " .
784 "is either missing or empty.\n";
785 }
786
787 my $busNum = $g_targetObj->getAttribute($i2c->{SOURCE}, "I2C_PORT");
788 if ($busNum =~ /0x/i) {
789 $busNum = hex($busNum);
790 }
791
792 #Convert the number to the Linux numbering scheme.
Matt Spinler30b461c2016-10-10 16:50:07 -0500793 $busNum += $g_i2cBusAdjust;
Matt Spinler74909132016-10-07 13:52:19 -0500794
795 #Get the compatible property
796 if ($g_targetObj->isBadAttribute($i2c->{DEST_PARENT},
797 "BMC_DT_COMPATIBLE")) {
798 die "ERROR: BMC_DT_COMPATIBLE attribute in $i2c->{DEST_PARENT} " .
799 "is either missing or empty.\n";
800 }
801
802 $deviceNode{compatible} = $g_targetObj->getAttribute(
803 $i2c->{DEST_PARENT},
804 "BMC_DT_COMPATIBLE");
805
806 #Get any other part specific properties, where the property
807 #names are actually defined in the XML.
808 my %props = getPartDefinedDTProperties($i2c->{DEST_PARENT});
809 foreach my $prop (sort keys %props) {
810 $deviceNode{$prop} = $props{$prop};
811 }
812
813 #busNodeName is the hash twice so when we loop
814 #below it doesn't get lost
815 my $busNodeName = "i2c$busNum";
816 $busNodes{$busNodeName}{$busNodeName}{status} = "okay";
817 $busNodes{$busNodeName}{$busNodeName}{$deviceName} = { %deviceNode };
Matt Spinler2efdcba2016-11-08 15:37:20 -0600818
819 #Add in any pinctrl properties. These would come from the parent
820 #of $i2c{SOURCE}, which would be a unit-pingroup-bmc if the
821 #pins for this connection are multi-function.
822 addPinCtrlProps($g_targetObj->getTargetParent($i2c->{SOURCE}),
823 \%{$busNodes{$busNodeName}{$busNodeName}});
Matt Spinler74909132016-10-07 13:52:19 -0500824 }
825
826 #Each bus gets its own hash entry in the array
827 for my $b (sort keys %busNodes) {
828 push @nodes, { %{$busNodes{$b}} };
829 }
830
831 return @nodes;
832}
833
834
835#Returns a hash of property names and values that should be stored in
836#the device tree node for this device. The names of the properties and
837#the attributes to find their values in are stored in the
838#BMC_DT_ATTR_NAMES attribute in the chip.
839# $chip = the chip target
Matt Spinler889343f2017-01-30 14:14:11 -0600840sub getPartDefinedDTProperties
Matt Spinler74909132016-10-07 13:52:19 -0500841{
842 my $chip = shift;
843 my %props;
844
845 if ($g_targetObj->isBadAttribute($chip, "BMC_DT_ATTR_NAMES")) {
846 return %props;
847 }
848
849 my $attr = $g_targetObj->getAttribute($chip, "BMC_DT_ATTR_NAMES");
850 $attr =~ s/\s//g;
851 my @names = split(',', $attr);
852
853 #There can be up to 4 entries in this attribute
854 for (my $i = 0; $i < scalar @names; $i += 2) {
855
856 #$names[$i] holds the name of the attribute.
857 #$names[$i+1] holds the name of the property to store its value in.
858 if (($names[$i] ne "NA") && ($names[$i] ne "")) {
859
860 my $val = $g_targetObj->getAttribute($chip, $names[$i]);
861
862 #if the value is empty, assume it's for a standalone property,
863 #which gets turned into: some-property;
864 if ($val eq "") {
865 $props{$names[$i+1]} = ZERO_LENGTH_PROPERTY;
866 }
867 else {
868 $props{$names[$i+1]} = "<$val>";
869 }
870 }
871 }
872
873 return %props;
874}
875
876
877#Convert the MRW I2C address into the format the dts needs
878# $addr = the I2C Address
Matt Spinler889343f2017-01-30 14:14:11 -0600879sub adjustI2CAddress
Matt Spinler74909132016-10-07 13:52:19 -0500880{
881 my $addr = shift;
882
883 #MRW holds the 8 bit value. We need the 7 bit one.
Matt Spinler96f8f242016-11-28 16:26:57 -0600884 $addr = $addr >> 1;
Matt Spinler74909132016-10-07 13:52:19 -0500885 $addr = sprintf("0x%X", $addr);
886 $addr = lc $addr;
887
888 return $addr;
889}
890
891
Matt Spinler30b461c2016-10-10 16:50:07 -0500892#Sets the global $g_i2cBusAdjust from the configuration file.
Matt Spinler889343f2017-01-30 14:14:11 -0600893sub getI2CBusAdjust
Matt Spinler74909132016-10-07 13:52:19 -0500894{
Matt Spinler30b461c2016-10-10 16:50:07 -0500895 if (exists $g_configuration{"i2c-bus-adjust"}) {
Matt Spinler74909132016-10-07 13:52:19 -0500896
Matt Spinler30b461c2016-10-10 16:50:07 -0500897 $g_i2cBusAdjust = $g_configuration{"i2c-bus-adjust"};
Matt Spinler74909132016-10-07 13:52:19 -0500898
Matt Spinler30b461c2016-10-10 16:50:07 -0500899 if (!looks_like_number($g_i2cBusAdjust)) {
900 die "ERROR: Invalid i2c-bus-adjust value $g_i2cBusAdjust " .
901 "found in config file.\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500902 }
903 }
Matt Spinler30b461c2016-10-10 16:50:07 -0500904 else {
905 $g_i2cBusAdjust = 0;
906 print "WARNING: No I2C Bus number adjustment done " .
907 "for this system.\n";
908 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500909}
910
911
Matt Spinler2efdcba2016-11-08 15:37:20 -0600912
913#Adds two pinctrl properties to the device node hash passed in,
914#if specified in the MRW. Pin Control refers to a mechanism for
915#Linux to know which function of a multi-function pin to configure.
916#For example, a pin could either be configured to be a GPIO, or
917#an I2C clock line. The pin function depends on board wiring,
918#so is known by the MRW.
919# $target = the target to get the BMC_DT_PINCTRL_FUNCTS attribute from
920# $node = a hash reference to the device tree node to add the properties to
Matt Spinler889343f2017-01-30 14:14:11 -0600921sub addPinCtrlProps
Matt Spinler2efdcba2016-11-08 15:37:20 -0600922{
923 my ($target, $node) = @_;
924
925 if (!$g_targetObj->isBadAttribute($target, "BMC_DT_PINCTRL_FUNCS")) {
926 my $attr = $g_targetObj->getAttribute($target,
927 "BMC_DT_PINCTRL_FUNCS");
928
929 my $pinCtrl0Prop = makePinCtrl0PropValue($attr);
930 if ($pinCtrl0Prop ne "") {
931 $node->{"pinctrl-names"} = "default";
932 $node->{"pinctrl-0"} = $pinCtrl0Prop;
933 }
934 }
935}
936
937
938#Constructs the pinctrl-0 property value based on the
939#BMC_DT_PINCTRL_FUNCS attribute passed in.
940# $attr = BMC_DT_PINCTRL_FUNCS attribute value, which is an array
Matt Spinler889343f2017-01-30 14:14:11 -0600941sub makePinCtrl0PropValue
Matt Spinler2efdcba2016-11-08 15:37:20 -0600942{
943 my $attr = shift;
944 my @entries;
945 my $value = "";
946
947 $attr =~ s/\s//g;
948 my @funcs = split(',', $attr);
949 foreach my $func (@funcs) {
950 if (($func ne "NA") && ($func ne "")) {
951 push @entries, $func;
952 }
953 }
954
955 #<&pinctrl_funcA_default &pinctrl_funcB_default ...>
956 if (scalar @entries) {
957 $value = "<";
958 foreach my $entry (@entries) {
959 $value .= "&pinctrl_".$entry."_default ";
960 }
961 $value =~ s/\s$//; #Remove the trailing space
962 $value .= ">";
963 }
964
965 return $value;
966}
967
968
Matt Spinler7d381e12016-09-27 14:27:24 -0500969#Returns a list of compatible fields for the BMC itself.
Matt Spinler889343f2017-01-30 14:14:11 -0600970sub getBMCCompatibles
Matt Spinler7d381e12016-09-27 14:27:24 -0500971{
972 my @compats;
973
Matt Spinler23d47c22016-10-04 12:31:21 -0500974 #1st entry: <system mfgr>,<system name>-bmc
975 #2nd entry: <bmc mfgr>,<bmc model>
Matt Spinler7d381e12016-09-27 14:27:24 -0500976
Matt Spinler23d47c22016-10-04 12:31:21 -0500977 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() }) {
978 if ($g_targetObj->getType($target) eq "SYS") {
979 my $mfgr = $g_targetObj->getAttribute($target, "MANUFACTURER");
980 push @compats, lc "$mfgr,$g_systemName-bmc";
981 last;
982 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500983 }
984
985 push @compats, lc($g_bmcMfgr).",".lc($g_bmcModel);
986
987 return @compats;
988}
989
990
991#Returns a string for the system's BMC model property
Matt Spinler889343f2017-01-30 14:14:11 -0600992sub getSystemBMCModel
Matt Spinler7d381e12016-09-27 14:27:24 -0500993{
Matt Spinler995f2a22016-09-30 13:07:31 -0500994 #'<System> BMC'
Matt Spinler7d381e12016-09-27 14:27:24 -0500995 my $sys = lc $g_systemName;
996 $sys = uc(substr($sys, 0, 1)) . substr($sys, 1);
997
998 return $sys . " BMC";
999}
1000
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001001#Create the comment that will show up in the device tree
1002#for a connection. In the output, will look like:
1003# // sourceUnit ->
1004# // destChip
1005#
1006# $conn = The connection hash reference
Matt Spinler889343f2017-01-30 14:14:11 -06001007sub connectionComment
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001008{
1009 my $conn = shift;
1010 my $comment = "$conn->{SOURCE} ->\n$conn->{DEST_PARENT}";
1011 return $comment;
1012}
1013
Matt Spinler7d381e12016-09-27 14:27:24 -05001014
1015#Prints a list of nodes at the same indent level
1016# $f = file handle
1017# $level = indent level (0,1,etc)
1018# @nodes = array of node hashes to print, where the
1019# key for the hash is the name of the node
Matt Spinler889343f2017-01-30 14:14:11 -06001020sub printNodes
Matt Spinler7d381e12016-09-27 14:27:24 -05001021{
1022 my ($f, $level, @nodes) = @_;
1023
1024 foreach my $n (@nodes) {
1025 my %node = %$n;
1026
1027 foreach my $name (sort keys %node) {
1028 my %n = %{ $node{$name} };
1029 printNode($f, $level, $name, %n);
1030 }
1031 }
1032}
1033
1034
1035#Print a single node and its children
1036# $f = file handle
1037# $level = indent level (0,1,etc)
1038# $name = the name of the node - shows up as:
1039# name { ...
1040# %vals = The contents of the node, with the following options:
1041# if the key is:
1042# - 'DTSI_INCLUDE', then value gets turned into a #include
Matt Spinler995f2a22016-09-30 13:07:31 -05001043# - 'COMMENT', then value gets turned into a // comment
Matt Spinler74909132016-10-07 13:52:19 -05001044# - 'ZERO_LENGTH_PROPERTY' then value gets turned into: value;
Matt Spinler7d381e12016-09-27 14:27:24 -05001045#
1046# If the value is:
1047# - a hash - then that hash gets turned into a child node
1048# where the key is the name of the child node
Matt Spinler995f2a22016-09-30 13:07:31 -05001049# - an array of hashes indicates an array of child nodes
Matt Spinler889343f2017-01-30 14:14:11 -06001050sub printNode
Matt Spinler7d381e12016-09-27 14:27:24 -05001051{
1052 my ($f, $level, $name, %vals) = @_;
1053 my $include = "";
1054
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001055 #No reason to print an empty node
1056 if (!keys %vals) {
1057 return;
1058 }
1059
Matt Spinler7d381e12016-09-27 14:27:24 -05001060 if ($level == 0) {
1061 $name = "&".$name;
1062 }
1063
Matt Spinler995f2a22016-09-30 13:07:31 -05001064 print $f "\n";
1065
1066 if (exists $vals{COMMENT}) {
1067 my @lines = split('\n', $vals{COMMENT});
1068 foreach my $l (@lines) {
1069 print $f indent($level) . "// $l\n";
1070 }
1071 }
1072
Matt Spinler6d391252017-01-31 13:46:06 -06001073 #The node can have a label, which looks like:
1074 #label : name {
1075 my $label = "";
1076 if (exists $vals{NODE_LABEL}) {
1077 $label = $vals{NODE_LABEL} . ": ";
1078 }
1079
1080 print $f indent($level) . $label . "$name {\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001081
Matt Spinler74909132016-10-07 13:52:19 -05001082 #First print properties, then includes, then subnodes
1083
1084 #Print Properties
Matt Spinler7d381e12016-09-27 14:27:24 -05001085 foreach my $v (sort keys %vals) {
1086
Matt Spinler995f2a22016-09-30 13:07:31 -05001087 next if ($v eq "COMMENT");
Matt Spinler74909132016-10-07 13:52:19 -05001088 next if ($v eq "DTSI_INCLUDE");
Matt Spinler6d391252017-01-31 13:46:06 -06001089 next if ($v eq "NODE_LABEL");
Matt Spinler74909132016-10-07 13:52:19 -05001090 next if (ref($vals{$v}) eq "HASH");
1091 next if (ref($vals{$v}) eq "ARRAY");
Matt Spinler995f2a22016-09-30 13:07:31 -05001092
Matt Spinler74909132016-10-07 13:52:19 -05001093 if ($vals{$v} ne ZERO_LENGTH_PROPERTY) {
1094 printProperty($f, $level+1, $v, $vals{$v});
Matt Spinler7d381e12016-09-27 14:27:24 -05001095 }
Matt Spinler74909132016-10-07 13:52:19 -05001096 else {
1097 printZeroLengthProperty($f, $level+1, $v);
1098 }
1099 }
1100
1101 #Print Includes
1102 foreach my $v (sort keys %vals) {
1103
1104 if ($v eq "DTSI_INCLUDE") {
1105 #print 1 include per line
1106 my @incs = split(',', $vals{$v});
1107 foreach my $i (@incs) {
1108 print $f qq(#include "$i";\n);
1109 }
1110 }
1111 }
1112
1113 #Print Nodes
1114 foreach my $v (sort keys %vals) {
1115
1116 if (ref($vals{$v}) eq "HASH") {
Matt Spinler7d381e12016-09-27 14:27:24 -05001117 printNode($f, $level+1, $v, %{$vals{$v}});
1118 }
Matt Spinler995f2a22016-09-30 13:07:31 -05001119 #An array of nested nodes
1120 elsif (ref($vals{$v}) eq "ARRAY") {
1121 my @array = @{$vals{$v}};
1122 &printNodes($f, $level+1, @array);
1123 }
Matt Spinler7d381e12016-09-27 14:27:24 -05001124 }
1125
1126 print $f indent($level) . "};\n";
1127}
1128
1129
1130#Prints a comma separated list of properties.
1131#e.g. a = "b, c, d";
1132# $f = file handle
1133# $level = indent level (0,1,etc)
1134# $name = name of property
1135# @vals = list of property values
Matt Spinler889343f2017-01-30 14:14:11 -06001136sub printPropertyList
Matt Spinler7d381e12016-09-27 14:27:24 -05001137{
1138 my ($f, $level, $name, @vals) = @_;
1139
1140 print $f indent($level) . "$name = ";
1141
1142 for (my $i = 0;$i < scalar @vals; $i++) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001143 print $f qq("$vals[$i]");
Matt Spinler7d381e12016-09-27 14:27:24 -05001144 if ($i < (scalar(@vals) - 1)) {
1145 print $f ", ";
1146 }
1147 }
1148 print $f ";\n"
1149}
1150
1151
1152#Prints a single property. e.g. a = "b";
1153# $f = file handle
1154# $level = indent level (0,1,etc)
1155# $name = name of property
1156# @vals = property values
Matt Spinler889343f2017-01-30 14:14:11 -06001157sub printProperty
Matt Spinler7d381e12016-09-27 14:27:24 -05001158{
1159 my ($f, $level, $name, $val) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001160 my $quoteChar = qq(");
Matt Spinler23d47c22016-10-04 12:31:21 -05001161
Matt Spinler30b461c2016-10-10 16:50:07 -05001162 $val = convertReference($val);
Matt Spinler23d47c22016-10-04 12:31:21 -05001163
1164 #properties with < > or single word aliases don't need quotes
1165 if (($val =~ /<.*>/) || ($val =~ /^&\w+$/)) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001166 $quoteChar = "";
Matt Spinler23d47c22016-10-04 12:31:21 -05001167 }
1168
Matt Spinler30b461c2016-10-10 16:50:07 -05001169 print $f indent($level) . "$name = $quoteChar$val$quoteChar;\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001170}
1171
1172
Matt Spinler30b461c2016-10-10 16:50:07 -05001173#Prints a zero length property e.g. some-property;
Matt Spinler7d381e12016-09-27 14:27:24 -05001174# $f = file handle
1175# $level = indent level (0,1,etc)
1176# $name = name of property
Matt Spinler889343f2017-01-30 14:14:11 -06001177sub printZeroLengthProperty
Matt Spinler7d381e12016-09-27 14:27:24 -05001178{
1179 my ($f, $level, $name) = @_;
1180 print $f indent($level) . "$name;\n";
1181}
1182
1183
Matt Spinler30b461c2016-10-10 16:50:07 -05001184#Replace '(ref)' with '&'.
Matt Spinler7d381e12016-09-27 14:27:24 -05001185#Needed because Serverwiz doesn't properly escape '&'s in the XML,
Matt Spinler30b461c2016-10-10 16:50:07 -05001186#so the '(ref)' string is used to represent the reference
Matt Spinler7d381e12016-09-27 14:27:24 -05001187#specifier instead of '&'.
Matt Spinler889343f2017-01-30 14:14:11 -06001188sub convertReference
1189{
Matt Spinler7d381e12016-09-27 14:27:24 -05001190 my $val = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -05001191 $val =~ s/\(ref\)/&/g;
Matt Spinler7d381e12016-09-27 14:27:24 -05001192 return $val
1193}
1194
1195
Matt Spinler7d381e12016-09-27 14:27:24 -05001196#Prints the device tree version line.
1197# $f = file handle
Matt Spinler889343f2017-01-30 14:14:11 -06001198sub printVersion
Matt Spinler7d381e12016-09-27 14:27:24 -05001199{
1200 my $f = shift;
1201 print $f VERSION."\n"
1202}
1203
1204
1205#Prints the #include line for pulling in an include file.
Matt Spinler30b461c2016-10-10 16:50:07 -05001206#The files to include come from the configuration file.
Matt Spinler7d381e12016-09-27 14:27:24 -05001207# $f = file handle
Matt Spinler30b461c2016-10-10 16:50:07 -05001208# $type = include type
Matt Spinler889343f2017-01-30 14:14:11 -06001209sub printIncludes
Matt Spinler7d381e12016-09-27 14:27:24 -05001210{
Matt Spinler30b461c2016-10-10 16:50:07 -05001211 my ($f, $type) = @_;
1212 my @includes = getIncludes($type);
Matt Spinler7d381e12016-09-27 14:27:24 -05001213
1214 foreach my $i (@includes) {
1215 #if a .dtsi, gets " ", otherwise < >
1216 if ($i =~ /\.dtsi$/) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001217 $i = qq("$i");
Matt Spinler7d381e12016-09-27 14:27:24 -05001218 }
1219 else {
Matt Spinler30b461c2016-10-10 16:50:07 -05001220 $i = "<$i>";
Matt Spinler7d381e12016-09-27 14:27:24 -05001221 }
Matt Spinler30b461c2016-10-10 16:50:07 -05001222 print $f "#include $i\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001223 }
1224}
1225
1226
Matt Spinler30b461c2016-10-10 16:50:07 -05001227#Returns an array of include files found in the config file
1228#for the type specified.
1229# $type = the include type, which is the section name in the
1230# YAML configuration file.
Matt Spinler889343f2017-01-30 14:14:11 -06001231sub getIncludes
Matt Spinler7d381e12016-09-27 14:27:24 -05001232{
Matt Spinler30b461c2016-10-10 16:50:07 -05001233 my $type = shift;
Matt Spinler7d381e12016-09-27 14:27:24 -05001234 my @includes;
1235
Matt Spinler30b461c2016-10-10 16:50:07 -05001236 #The config file may have a section but no includes
1237 #listed in it, which is OK.
1238 if ((exists $g_configuration{includes}{$type}) &&
1239 (ref($g_configuration{includes}{$type}) eq "ARRAY")) {
Matt Spinler7d381e12016-09-27 14:27:24 -05001240
Matt Spinler30b461c2016-10-10 16:50:07 -05001241 @includes = @{$g_configuration{includes}{$type}};
Matt Spinler7d381e12016-09-27 14:27:24 -05001242 }
1243
1244 return @includes;
1245}
1246
Matt Spinler30b461c2016-10-10 16:50:07 -05001247
Matt Spinler74909132016-10-07 13:52:19 -05001248#Appends the first value of the 'reg' property
1249#passed in to the name passed in to create the
1250#full name for the node
1251# $name = node name that will be appended to
1252# $reg = the reg property values
Matt Spinler889343f2017-01-30 14:14:11 -06001253sub makeNodeName
Matt Spinler74909132016-10-07 13:52:19 -05001254{
1255 my ($name, $reg) = @_;
1256
1257 $reg =~ s/<//g;
1258 $reg =~ s/>//g;
1259 my @vals = split(' ', $reg);
1260
1261 if (scalar @vals > 0) {
1262 $vals[0] =~ s/0x//;
1263 $name .= "@" . lc $vals[0];
1264 }
1265
1266 return $name;
1267}
1268
Matt Spinler7d381e12016-09-27 14:27:24 -05001269
1270#Prints the root node starting bracket.
1271# $f = file handle
Matt Spinler889343f2017-01-30 14:14:11 -06001272sub printRootNodeStart
1273{
Matt Spinler7d381e12016-09-27 14:27:24 -05001274 my $f = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -05001275 print $f qq(/ {\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001276}
1277
1278
1279#Prints the root node ending bracket.
1280# $f = file handle
1281# $level = indent level (0,1,etc)
Matt Spinler889343f2017-01-30 14:14:11 -06001282sub printRootNodeEnd
1283{
Matt Spinler7d381e12016-09-27 14:27:24 -05001284 my ($f, $level) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001285 print $f indent($level).qq(};\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001286}
1287
1288
1289#Returns a string that can be used to indent based on the
1290#level passed in. Each level is an additional 4 spaces.
1291# $level = indent level (0,1,etc)
Matt Spinler889343f2017-01-30 14:14:11 -06001292sub indent
1293{
Matt Spinler7d381e12016-09-27 14:27:24 -05001294 my $level = shift;
1295 return ' ' x ($level * 4);
1296}
1297
1298
Matt Spinler7d381e12016-09-27 14:27:24 -05001299sub printUsage
1300{
Matt Spinler30b461c2016-10-10 16:50:07 -05001301 print "gen_devtree.pl -x [XML filename] -y [yaml config file] " .
1302 "-o [output filename]\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001303 exit(1);
1304}