blob: d4f27e8cbb8b44a654ec5edb77ca6ea24628d372 [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 Spinler908e1822017-01-31 14:02:58 -060041my $g_pnorNodeName = undef;
Matt Spinler30b461c2016-10-10 16:50:07 -050042my %g_configuration = %{ LoadFile($configFile) };
43
Matt Spinler7d381e12016-09-27 14:27:24 -050044my $g_targetObj = Targets->new;
45$g_targetObj->loadXML($serverwizFile);
46
Matt Spinler74909132016-10-07 13:52:19 -050047my ($g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName);
48setGlobalAttributes();
Matt Spinler7d381e12016-09-27 14:27:24 -050049
Matt Spinler30b461c2016-10-10 16:50:07 -050050my $g_i2cBusAdjust = 0;
51getI2CBusAdjust();
Matt Spinler7d381e12016-09-27 14:27:24 -050052
53open (my $f, ">$outputFile") or die "Could not open $outputFile\n";
54
55printVersion($f);
Matt Spinler30b461c2016-10-10 16:50:07 -050056printIncludes($f, PRE_ROOT_INCLUDES);
Matt Spinler7d381e12016-09-27 14:27:24 -050057printRootNodeStart($f);
58
59printPropertyList($f, 1, "model", getSystemBMCModel());
Matt Spinler7d381e12016-09-27 14:27:24 -050060printPropertyList($f, 1, "compatible", getBMCCompatibles());
Matt Spinler995f2a22016-09-30 13:07:31 -050061
Matt Spinler23d47c22016-10-04 12:31:21 -050062printNode($f, 1, "aliases", getAliases());
Matt Spinler7d381e12016-09-27 14:27:24 -050063printNode($f, 1, "chosen", getChosen());
Matt Spinler30b461c2016-10-10 16:50:07 -050064printNode($f, 1, "memory", getBmcMemory());
Matt Spinler6d391252017-01-31 13:46:06 -060065printNode($f, 1, "reserved-memory", getReservedMemory());
Matt Spinler7d381e12016-09-27 14:27:24 -050066
Matt Spinler995f2a22016-09-30 13:07:31 -050067printNode($f, 1, "leds", getLEDNode());
68
Matt Spinler30b461c2016-10-10 16:50:07 -050069printIncludes($f, ROOT_INCLUDES);
70
Matt Spinler7d381e12016-09-27 14:27:24 -050071printRootNodeEnd($f, 0);
72
Matt Spinler96f8f242016-11-28 16:26:57 -060073printNodes($f, 0, getBMCFlashNodes());
Matt Spinler96f8f242016-11-28 16:26:57 -060074printNodes($f, 0, getOtherFlashNodes());
75
Matt Spinler908e1822017-01-31 14:02:58 -060076printNode($f, 0, "lpc_ctrl", getLPCNode());
77printNode($f, 0, "mbox", getMBoxNode());
78
Matt Spinler74909132016-10-07 13:52:19 -050079printNodes($f, 0, getI2CNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050080printNodes($f, 0, getMacNodes());
Matt Spinler995f2a22016-09-30 13:07:31 -050081printNodes($f, 0, getUARTNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050082printNodes($f, 0, getVuartNodes());
83
Matt Spinler30b461c2016-10-10 16:50:07 -050084printIncludes($f, POST_ROOT_INCLUDES);
85
Matt Spinler7d381e12016-09-27 14:27:24 -050086close $f;
87exit 0;
88
89
Matt Spinler74909132016-10-07 13:52:19 -050090#Finds the values for these globals:
91# $g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName
Matt Spinler889343f2017-01-30 14:14:11 -060092sub setGlobalAttributes
Matt Spinler74909132016-10-07 13:52:19 -050093{
Matt Spinlere2bf4392017-01-30 13:16:28 -060094 $g_bmc = Util::getBMCTarget($g_targetObj);
Matt Spinler74909132016-10-07 13:52:19 -050095
96 if ($g_targetObj->isBadAttribute($g_bmc, "MODEL")) {
97 die "The MODEL attribute on $g_bmc is missing or empty.\n";
98 }
99 $g_bmcModel = $g_targetObj->getAttribute($g_bmc, "MODEL");
100
101 if ($g_targetObj->isBadAttribute($g_bmc, "MANUFACTURER")) {
102 die "The MANUFACTURER attribute on $g_bmc is missing or empty.\n";
103 }
104 $g_bmcMfgr = $g_targetObj->getAttribute($g_bmc, "MANUFACTURER");
105
106 $g_systemName = $g_targetObj->getSystemName();
107 if (length($g_systemName) == 0) {
108 die "The SYSTEM_NAME attribute is not set on the system target.\n";
109 }
110}
111
112
Matt Spinler23d47c22016-10-04 12:31:21 -0500113#Returns a hash that represents the 'aliases' node.
114#Will look like:
115# aliases {
116# name1 = &val1;
117# name2 = &val2;
118# ...
119# }
Matt Spinler889343f2017-01-30 14:14:11 -0600120sub getAliases
Matt Spinler23d47c22016-10-04 12:31:21 -0500121{
122 my %aliases;
Matt Spinler23d47c22016-10-04 12:31:21 -0500123
Matt Spinler30b461c2016-10-10 16:50:07 -0500124 #Get the info from the config file
Matt Spinler23d47c22016-10-04 12:31:21 -0500125
Matt Spinler30b461c2016-10-10 16:50:07 -0500126 if ((not exists $g_configuration{aliases}) ||
127 (keys %{$g_configuration{aliases}} == 0)) {
128 print "WARNING: Missing or empty 'aliases' section in config file.\n";
129 return %aliases;
130 }
131 %aliases = %{ $g_configuration{aliases} };
132
133 #add a & reference if one is missing
134 foreach my $a (keys %aliases) {
135 if (($aliases{$a} !~ /^&/) && ($aliases{$a} !~ /^\(ref\)/)) {
136 $aliases{$a} = "(ref)$aliases{$a}";
Matt Spinler23d47c22016-10-04 12:31:21 -0500137 }
138 }
139
140 return %aliases;
141}
142
Matt Spinler7d381e12016-09-27 14:27:24 -0500143
144#Return a hash that represents the 'chosen' node
Matt Spinler995f2a22016-09-30 13:07:31 -0500145#Will look like:
146# chosen {
147# stdout-path = ...
148# bootargs = ...
149# }
Matt Spinler889343f2017-01-30 14:14:11 -0600150sub getChosen
Matt Spinler7d381e12016-09-27 14:27:24 -0500151{
Matt Spinler7d381e12016-09-27 14:27:24 -0500152 my %chosen;
Matt Spinler30b461c2016-10-10 16:50:07 -0500153 my @allowed = qw(bootargs stdin-path stdout-path);
154
155 #Get the info from the config file
156
157 if (not exists $g_configuration{chosen}) {
158 die "ERROR: Missing 'chosen' section in config file.\n";
159 }
160 %chosen = %{ $g_configuration{chosen} };
161
Matt Spinler30b461c2016-10-10 16:50:07 -0500162 foreach my $key (keys %chosen) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500163
Matt Spinler28fb1a92017-01-30 12:54:10 -0600164 #Check for allowed entries. Empty is OK.
165 if (!grep(/^$key$/, @allowed)) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500166 die "Invalid entry $key in 'chosen' section in config file\n";
167 }
Matt Spinler28fb1a92017-01-30 12:54:10 -0600168
169 #stdout-path and stdin-path can use aliases, which will look like
170 #(alias)uart5 in the yaml. Change to (ref)uart5 so it will be
171 #converted to a '&' later.
172 $chosen{$key} =~ s/\(alias\)/\(ref\)/g;
Matt Spinler30b461c2016-10-10 16:50:07 -0500173 }
174
Matt Spinler7d381e12016-09-27 14:27:24 -0500175 return %chosen;
176}
177
178
Matt Spinler30b461c2016-10-10 16:50:07 -0500179#Return a hash that represents the 'memory' node.
Matt Spinler995f2a22016-09-30 13:07:31 -0500180#Will look like:
Matt Spinler30b461c2016-10-10 16:50:07 -0500181# memory {
182# reg = < base size >
183# }
Matt Spinler889343f2017-01-30 14:14:11 -0600184sub getBmcMemory
Matt Spinler30b461c2016-10-10 16:50:07 -0500185{
186 my %memory;
187
188 #Get the info from the config file
189
190 if (not exists $g_configuration{memory}) {
191 die "ERROR: Missing 'memory' section in config file.\n";
192 }
193
194 if ((not exists $g_configuration{memory}{base}) ||
195 ($g_configuration{memory}{base} !~ /0x/)) {
196 die "ERROR: The base entry in the memory section in the config " .
197 "file is either missing or invalid.\n";
198 }
199
200 if ((not exists $g_configuration{memory}{size}) ||
201 ($g_configuration{memory}{size} !~ /0x/)) {
202 die "ERROR: The size entry in the memory section in the config " .
203 "file is either missing or invalid.\n";
204 }
205
206 #Future: could do more validation on the actual values
207
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600208 addRegProp(\%memory,
209 $g_configuration{memory}{base},
210 $g_configuration{memory}{size});
Matt Spinler30b461c2016-10-10 16:50:07 -0500211
212 return %memory;
213}
214
215
Matt Spinler6d391252017-01-31 13:46:06 -0600216#Returns a hash that represents the 'reserved-memory' node.
217#This currently only supports the memory region for the LPC
218#host spi flash mailbox. Will look like:
219# reserved-memory {
220# #address-cells = <1>;
221# #size-cells = <1>;
222# ranges;
223#
224# flash_memory: region@94000000 {
225# no-map;
226# reg = <0x94000000 0x04000000>;
227# };
228# };
229sub getReservedMemory
230{
231 my %memory;
232
233 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
234 return %memory;
235 }
236
237 $memory{"#address-cells"} = "<1>";
238 $memory{"#size-cells"} = "<1>";
239 $memory{ranges} = ZERO_LENGTH_PROPERTY;
240
241 #Get the sub node that contains the address range
242 my ($name, $node) = getHostSpiFlashMboxRegion();
243 $memory{$name} = { %$node };
244
245 return %memory;
246}
247
248
249#Returns a hash that represents a child node of the
250#reserved-memory node which contains the address range
251#that the host spi flash is mapped to.
252sub getHostSpiFlashMboxRegion
253{
254 my %node;
255
256 $node{"no-map"} = ZERO_LENGTH_PROPERTY;
257
258 #This node needs a label the LPC node can refer to.
259 $node{NODE_LABEL} = HOST_SPI_FLASH_MEM_REGION_NODE_LABEL;
260
261 #Get the memory region's base address and size from the config file
262 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}
263 {"bmc-address-range"}{base}) {
264 die "Could not find lpc-host-spi-flash-mailbox base " .
265 "address in config file\n";
266 }
267
268 my $base = $g_configuration{"lpc-host-spi-flash-mailbox"}
269 {"bmc-address-range"}{base};
270 #Allow 1 hex value, up to 4B
271 if ($base !~ /^0x[0-9a-fA-F]{1,8}$/) {
272 die "lpc-host-spi-flash-mailbox base address $base is invalid\n";
273 }
274
275 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}
276 {"bmc-address-range"}{size}) {
277 die "Could not find lpc-host-spi-flash-mailbox address size " .
278 "in config file\n";
279 }
280
281 my $size = $g_configuration{"lpc-host-spi-flash-mailbox"}
282 {"bmc-address-range"}{size};
283 if ($size !~ /^0x[0-9a-fA-F]{1,8}$/) {
284 die "lpc-host-spi-flash-mailbox address range size " .
285 "$size is invalid\n";
286 }
287
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600288 addRegProp(\%node, $base, $size);
Matt Spinler6d391252017-01-31 13:46:06 -0600289 my $name = makeNodeName("region", $node{reg});
290
291 return ($name, \%node);
292}
293
294
Matt Spinler25d60bb2016-10-31 15:16:03 -0500295#Returns an array of hashes representing the device tree nodes for
296#the BMC flash. These nodes are BMC model specific because different
297#models can have different device drivers.
Matt Spinler889343f2017-01-30 14:14:11 -0600298sub getBMCFlashNodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500299{
Matt Spinler25d60bb2016-10-31 15:16:03 -0500300 my @nodes;
301
302 if ($g_bmcModel eq "AST2500") {
303 my %node = getAST2500BMCSPIFlashNode();
304 push @nodes, { %node };
305 }
306 else {
307 die "ERROR: No BMC SPI flash support yet for BMC model $g_bmcModel\n";
308 }
309
310 return @nodes;
311}
312
313
314#Returns a hash that represents the BMC SPI flash(es) by finding the SPI
315#connections that come from the unit tagged as BMC_CODE. The code also
316#looks in the config file for any additional properties to add. Supports
317#the hardware where the same SPI master unit can be wired to more than 1
318#flash (a chip select line is used to switch between them.) This is
319#specific to the ASPEED AST2500 hardware and device driver.
320#Will look like:
321# fmc {
322# status = "okay"
323# flash@0 {
324# ...
325# };
326# flash@1 {
327# ...
328# };
Matt Spinler889343f2017-01-30 14:14:11 -0600329sub getAST2500BMCSPIFlashNode
Matt Spinler25d60bb2016-10-31 15:16:03 -0500330{
331 my %bmcFlash;
332 my $chipSelect = 0;
333 my $lastUnit = "";
334
Matt Spinler18d5f572016-11-15 15:25:45 -0600335 my $connections = $g_targetObj->findConnections($g_bmc, "SPI", "FLASH");
Matt Spinler25d60bb2016-10-31 15:16:03 -0500336
337 if ($connections eq "") {
338 die "ERROR: No BMC SPI flashes found connected to the BMC\n";
339 }
340
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600341 statusOK(\%{$bmcFlash{fmc}});
Matt Spinler25d60bb2016-10-31 15:16:03 -0500342
343 foreach my $spi (@{$connections->{CONN}}) {
344
345 #Looking for spi-masters with a function of 'BMC_CODE'.
346 #It's possible there are multiple flash chips here.
347 if (!$g_targetObj->isBadAttribute($spi->{SOURCE}, "SPI_FUNCTION")) {
348
349 my $function = $g_targetObj->getAttribute($spi->{SOURCE},
350 "SPI_FUNCTION");
351 if ($function eq "BMC_CODE") {
352
353 my $flashName = "flash@".$chipSelect;
354
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500355 $bmcFlash{fmc}{$flashName}{COMMENT} = connectionComment($spi);
Matt Spinler25d60bb2016-10-31 15:16:03 -0500356
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600357 statusOK(\%{$bmcFlash{fmc}{$flashName}});
Matt Spinler25d60bb2016-10-31 15:16:03 -0500358
359 #Add in anything specified in the config file for this chip.
360 addBMCFlashConfigProperties(\%{$bmcFlash{fmc}{$flashName}},
361 $chipSelect);
362
363 #The code currently only supports the config where a chip
364 #select line is used to select between possibly multiple
365 #flash chips attached to the same SPI pins/unit. So we
366 #need to make sure if there are multiple chips found, that
367 #they are off of the same master unit.
368 if ($lastUnit eq "") {
369 $lastUnit = $spi->{SOURCE};
370 }
371 else {
372 if ($lastUnit ne $spi->{SOURCE}) {
373 die "ERROR: Currently only 1 spi-master unit is " .
374 "supported for BMC flash connections."
375 }
376 }
377
378 #Since we don't need anything chip select specific from the
379 #XML, we can just assign our own chip selects.
380 $chipSelect++;
381 }
382 }
383 }
384
385 if ($chipSelect == 0) {
386 die "ERROR: Didn't find any BMC flash chips connected";
387 }
388
389 return %bmcFlash;
390}
391
392
393#Looks in the bmc-flash-config section in the config file for the
394#chip select passed in to add any additional properties to the BMC
395#flash node.
396# $node = hash reference to the flash node
397# $cs = the flash chip select value
Matt Spinler889343f2017-01-30 14:14:11 -0600398sub addBMCFlashConfigProperties
Matt Spinler25d60bb2016-10-31 15:16:03 -0500399{
400 my ($node, $cs) = @_;
401 my $section = "chip-select-$cs";
402
403 if (exists $g_configuration{"bmc-flash-config"}{$section}) {
404 foreach my $key (sort keys $g_configuration{"bmc-flash-config"}{$section}) {
405 $node->{$key} = $g_configuration{"bmc-flash-config"}{$section}{$key};
406 }
407 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500408}
409
410
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500411#Returns an array of hashes representing the other flashes used by the
412#BMC besides the ones that hold the BMC code. This is BMC model specific
413#as different models can have different interfaces.
414#Typically, these are SPI flashes.
Matt Spinler889343f2017-01-30 14:14:11 -0600415sub getOtherFlashNodes
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500416{
417 my @nodes;
418
419 if ($g_bmcModel eq "AST2500") {
420 @nodes = getAST2500SpiFlashNodes();
421 }
422 else {
423 die "ERROR: No SPI flash support yet for BMC model $g_bmcModel\n";
424 }
425
426 return @nodes;
427}
428
429
430#Returns an array of hashes representing the SPI flashes in an
431#AST2500. These are for the SPI1 and SPI2 interfaces in the chip.
432#Each SPI master interface can support multiple flash chips. If
433#no hardware is connected to the interface, the node won't be present.
Matt Spinler889343f2017-01-30 14:14:11 -0600434sub getAST2500SpiFlashNodes
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500435{
436 my @nodes;
437
438 #The AST2500 has 2 SPI master units, 1 and 2.
439 my @units = (1, 2);
440
441 foreach my $unit (@units) {
442
Matt Spinler908e1822017-01-31 14:02:58 -0600443 my ($node, $foundPNOR) = getAST2500SpiMasterNode($unit);
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500444
Matt Spinler908e1822017-01-31 14:02:58 -0600445 if (keys %$node) {
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500446 my %spiNode;
447 my $nodeName = "spi$unit";
Matt Spinler908e1822017-01-31 14:02:58 -0600448 $spiNode{$nodeName} = { %$node };
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500449 push @nodes, { %spiNode };
Matt Spinler908e1822017-01-31 14:02:58 -0600450
451 #Save off the PNOR SPI node name for use by LPC node
452 if ($foundPNOR) {
453 $g_pnorNodeName = $nodeName;
454 }
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500455 }
456 }
457
458 return @nodes;
459}
460
461
462#Returns a hash that represents the device tree node for the SPI1
463#or SPI2 master interface on the AST2500. Each master can support
464#multiple chips by use of a chip select.
465#Will look like:
466# spi1 {
467# status = "okay";
468# flash@0 {
469# ...
470# };
471# };
472#
473# $spiNum = The SPI master unit number to use
Matt Spinler889343f2017-01-30 14:14:11 -0600474sub getAST2500SpiMasterNode
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500475{
476 my $spiNum = shift;
477 my %spiMaster;
478 my $chipSelect = 0;
Matt Spinler908e1822017-01-31 14:02:58 -0600479 my $foundPNOR = 0;
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500480
Matt Spinler18d5f572016-11-15 15:25:45 -0600481 my $connections = $g_targetObj->findConnections($g_bmc, "SPI", "FLASH");
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500482
483 if ($connections eq "") {
484 return %spiMaster;
485 }
486
487 #Looking for spi-masters with a chip-unit of $spiNum
488 #It's possible there are multiple flash chips off the master
489 foreach my $spi (@{$connections->{CONN}}) {
490
491 my $unitNum = $g_targetObj->getAttribute($spi->{SOURCE},
492 "CHIP_UNIT");
493 if ($unitNum == $spiNum) {
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600494 statusOK(\%spiMaster);
Matt Spinler2efdcba2016-11-08 15:37:20 -0600495
496 #Add in any pinctrl properties. These would come from the parent
497 #of $spi{SOURCE}, which would be a unit-pingroup-bmc if the
498 #pins for this connection are multi-function.
499 addPinCtrlProps($g_targetObj->getTargetParent($spi->{SOURCE}),
500 \%spiMaster);
501
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500502 my $flashName = "flash@".$chipSelect;
503
504 $spiMaster{$flashName}{COMMENT} = connectionComment($spi);
505
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600506 statusOK(\%{$spiMaster{$flashName}});
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500507
Matt Spinler0eda4882016-11-30 15:20:11 -0600508 #AST2500 PNORs need a label
509 my $function = $g_targetObj->getAttribute($spi->{SOURCE},
510 "SPI_FUNCTION");
511 if ($function eq "PNOR") {
512 $spiMaster{$flashName}{label} = "pnor";
Matt Spinler908e1822017-01-31 14:02:58 -0600513 $foundPNOR = 1;
Matt Spinler0eda4882016-11-30 15:20:11 -0600514 }
515
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500516 $chipSelect++;
517 }
518 }
519
Matt Spinler908e1822017-01-31 14:02:58 -0600520 return (\%spiMaster, $foundPNOR);
521}
522
523
524#Returns a hash that represents the mbox node.
525#This node is used by the LPC mailbox device driver.
526#Only present if the LPC mailbox is enabled in the config file.
527#Node looks like:
528# &mbox {
529# status = "okay";
530# }
531sub getMBoxNode
532{
533 my %node;
534 if (exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600535 statusOK(\%node);
Matt Spinler908e1822017-01-31 14:02:58 -0600536 }
537
538 return %node;
539}
540
541
542#Returns a hash that represents the LPC node.
543#Only present if the LPC mailbox is enabled in the config file.
544#Node looks like:
545# &lpc_ctrl {
546# flash = <&spi1>;
547# memory-region = <&flash_memory>;
548# status = "okay";
549#};
550sub getLPCNode
551{
552 my %node;
553 if (exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
554
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600555 statusOK(\%node);
Matt Spinler908e1822017-01-31 14:02:58 -0600556
557 #Point to the reserved-memory region label
558 $node{"memory-region"} = "<(ref)" .
559 HOST_SPI_FLASH_MEM_REGION_NODE_LABEL . ">";
560
561 if (not defined $g_pnorNodeName) {
562 die "The PNOR SPI flash node cannot be found but is required " .
563 "if the LPC mailbox is enabled.\n";
564 }
565
566 $node{flash} = "<(ref)$g_pnorNodeName>";
567 }
568
569 return %node;
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500570}
571
572
Matt Spinler995f2a22016-09-30 13:07:31 -0500573#Returns a hash that represents the leds node by finding all of the
574#GPIO connections to LEDs.
575#Node will look like:
576# leds {
577# <ledname> {
578# gpios = &gpio ASPEED_GPIO(x, y) GPIO_ACTIVE_xxx>
579# };
580# <another ledname> {
581# ...
582# }
Matt Spinler889343f2017-01-30 14:14:11 -0600583sub getLEDNode
Matt Spinler995f2a22016-09-30 13:07:31 -0500584{
585 my %leds;
586
Matt Spinlereca7f062016-11-07 09:59:23 -0600587 $leds{compatible} = "gpio-leds";
Matt Spinler995f2a22016-09-30 13:07:31 -0500588
Matt Spinler18d5f572016-11-15 15:25:45 -0600589 my $connections = $g_targetObj->findConnections($g_bmc, "GPIO", "LED");
Matt Spinler995f2a22016-09-30 13:07:31 -0500590
591 if ($connections eq "") {
592 print "WARNING: No LEDs found connected to the BMC\n";
593 return %leds;
594 }
595
596 foreach my $gpio (@{$connections->{CONN}}) {
597 my %ledNode;
598
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500599 $ledNode{COMMENT} = connectionComment($gpio);
Matt Spinler995f2a22016-09-30 13:07:31 -0500600
601 #The node name will be the simplified LED name
602 my $name = $gpio->{DEST_PARENT};
603 $name =~ s/(-\d+$)//; #remove trailing position
604 $name =~ s/.*\///; #remove the front of the path
605
606 #For now only supports ASPEED.
607 if (uc($g_bmcMfgr) ne "ASPEED") {
608 die "ERROR: Unsupported BMC manufacturer $g_bmcMfgr\n";
609 }
610 my $num = $g_targetObj->getAttribute($gpio->{SOURCE}, "PIN_NUM");
611 my $macro = getAspeedGpioMacro($num);
612
613 #If it's active high or low
614 my $state = $g_targetObj->getAttribute($gpio->{DEST_PARENT}, "ON_STATE");
615 my $activeString = getGpioActiveString($state);
616
617 $ledNode{gpios} = "<&gpio $macro $activeString>";
618
619 $leds{$name} = { %ledNode };
620 }
621
622 return %leds;
623}
624
625
626#Returns a either GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW
627# $val = either a 1 or a 0 for active high or low
Matt Spinler889343f2017-01-30 14:14:11 -0600628sub getGpioActiveString
629{
Matt Spinler995f2a22016-09-30 13:07:31 -0500630 my $val = shift;
631
632 if ($val == 0) {
633 return "GPIO_ACTIVE_LOW";
634 }
635
636 return "GPIO_ACTIVE_HIGH";
637}
638
639
640#Turns a GPIO number into something like ASPEED_GPIO(A, 0) for the
641#ASPEED GPIO numbering scheme A[0-7] -> Z[0-7] and then starts at
642#AA[0-7] after that.
643# $num = the GPIO number
Matt Spinler889343f2017-01-30 14:14:11 -0600644sub getAspeedGpioMacro
645{
Matt Spinler995f2a22016-09-30 13:07:31 -0500646 my $num = shift;
647 my $char;
648 my $offset = $num % 8;
649 my $block = int($num / 8);
650
651 #If past Z, wraps to AA, AB, etc
652 if ((ord('A') + $block) > ord('Z')) {
653 #how far past Z?
654 $char = $block - (ord('Z') - ord('A'));
655
656 #Don't let it wrap twice
657 if ($char > (ord('Z') - ord('A') + 1)) {
658 die "ERROR: Invalid PIN_NUM value $num found for GPIO\n";
659 }
660
661 #start back at 'A' again, and convert to a character
662 $char = chr($char + ord('A') - 1);
663
664 #Add in a bonus 'A', to get something like AB
665 $char = "A".$char;
666 }
667 else {
668 $char = ord('A') + $block;
669 $char = chr($char);
670 }
671
672 return "ASPEED_GPIO($char, $offset)";
673}
674
675
676#Returns a list of hashes that represent the UART nodes on the BMC by
677#finding the UART connections.
678#Nodes will look like:
679# &uartX {
680# status = "okay"
681# }
Matt Spinler889343f2017-01-30 14:14:11 -0600682sub getUARTNodes
Matt Spinler995f2a22016-09-30 13:07:31 -0500683{
684 my @nodes;
685
Matt Spinler23d47c22016-10-04 12:31:21 -0500686 #Using U750 for legacy MRW reasons
Matt Spinler18d5f572016-11-15 15:25:45 -0600687 my $connections = $g_targetObj->findConnections($g_bmc, "U750");
Matt Spinler995f2a22016-09-30 13:07:31 -0500688
689 if ($connections eq "") {
690 print "WARNING: No UART buses found connected to the BMC\n";
691 return @nodes;
692 }
693
694 foreach my $uart (@{$connections->{CONN}}) {
695 my %node;
696
697 my $num = $g_targetObj->getAttribute($uart->{SOURCE}, "CHIP_UNIT");
698 my $name = "uart$num";
699
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600700 statusOK(\%{$node{$name}});
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500701 $node{$name}{COMMENT} = connectionComment($uart);
Matt Spinler995f2a22016-09-30 13:07:31 -0500702
Matt Spinler2efdcba2016-11-08 15:37:20 -0600703 #Add in any pinctrl properties. These would come from the parent
704 #of $uart{SOURCE}, which would be a unit-pingroup-bmc if the
705 #pins for this connection are multi-function.
706 addPinCtrlProps($g_targetObj->getTargetParent($uart->{SOURCE}),
707 \%{$node{$name}});
708
Matt Spinler995f2a22016-09-30 13:07:31 -0500709 push @nodes, { %node };
710 }
711
Matt Spinler7d381e12016-09-27 14:27:24 -0500712 return @nodes;
713}
714
715
Matt Spinler995f2a22016-09-30 13:07:31 -0500716#Returns a list of hashes that represent the MAC (ethernet) nodes on the BMC
717#by finding the connections of type ETHERNET.
718#Nodes will look like:
719# &macX {
720# ...
721# }
Matt Spinler889343f2017-01-30 14:14:11 -0600722sub getMacNodes
Matt Spinler995f2a22016-09-30 13:07:31 -0500723{
724 my @nodes;
725
Matt Spinler18d5f572016-11-15 15:25:45 -0600726 my $connections = $g_targetObj->findConnections($g_bmc, "ETHERNET");
Matt Spinler995f2a22016-09-30 13:07:31 -0500727
728 if ($connections eq "") {
729 print "WARNING: No ethernet buses found connected to the BMC\n";
730 return @nodes;
731 }
732
733 foreach my $eth (@{$connections->{CONN}}) {
734 my %node;
735
736 my $num = $g_targetObj->getAttribute($eth->{SOURCE}, "CHIP_UNIT");
737 my $ncsi = $g_targetObj->getAttribute($eth->{SOURCE}, "NCSI_MODE");
738 my $hwChecksum = $g_targetObj->getAttribute($eth->{SOURCE},
739 "USE_HW_CHECKSUM");
740
741 my $name = "mac$num";
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600742 statusOK(\%{$node{$name}});
Matt Spinler995f2a22016-09-30 13:07:31 -0500743
744 if ($ncsi == 1) {
Matt Spinler74909132016-10-07 13:52:19 -0500745 $node{$name}{"use-ncsi"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500746 }
747 if ($hwChecksum == 0) {
Matt Spinler74909132016-10-07 13:52:19 -0500748 $node{$name}{"no-hw-checksum"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500749 }
750
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500751 $node{$name}{COMMENT} = connectionComment($eth);
Matt Spinler995f2a22016-09-30 13:07:31 -0500752
Matt Spinler2efdcba2016-11-08 15:37:20 -0600753 #Add in any pinctrl properties. These would come from the parent
754 #of $eth{SOURCE}, which would be a unit-pingroup-bmc if the
755 #pins for this connection are multi-function.
756 addPinCtrlProps($g_targetObj->getTargetParent($eth->{SOURCE}),
757 \%{$node{$name}});
758
Matt Spinler995f2a22016-09-30 13:07:31 -0500759 push @nodes, { %node };
760 }
761
762 return @nodes;
763}
764
765
766#Returns a list of hashes that represent the virtual UART nodes
767#Node will look like:
768# &vuart {
769# status = "okay"
770# }
Matt Spinler889343f2017-01-30 14:14:11 -0600771sub getVuartNodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500772{
773 my @nodes;
774 my %node;
775
776 #For now, enable 1 node all the time.
Matt Spinler995f2a22016-09-30 13:07:31 -0500777 #TBD if this needs to be fixed
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600778 statusOK(\%{$node{vuart}});
Matt Spinler7d381e12016-09-27 14:27:24 -0500779
780 push @nodes, { %node };
781
782 return @nodes;
783}
784
Matt Spinler74909132016-10-07 13:52:19 -0500785#Returns a list of hashes that represent the I2C device nodes.
786#There is 1 parent node for each bus, which then have subnodes
787#for each device on that bus. If a bus doesn't have any
788#attached devices, it doesn't need to show up.
789#The nodes will look like:
790# &i2c0 {
791# status = "okay"
792# device1@addr { (addr = 7 bit I2C address)
793# reg = <addr>
794# compatible = ...
795# ...
796# }
797# device2@addr {
798# reg = <addr>
799# ...
800# }
801# }
802# &i2c1 {
803# ...
804# }
Matt Spinler889343f2017-01-30 14:14:11 -0600805sub getI2CNodes
Matt Spinler74909132016-10-07 13:52:19 -0500806{
807 my @nodes;
808 my %busNodes;
809
Matt Spinler18d5f572016-11-15 15:25:45 -0600810 my $connections = $g_targetObj->findConnections($g_bmc, "I2C");
Matt Spinler74909132016-10-07 13:52:19 -0500811
812 if ($connections eq "") {
813 print "WARNING: No I2C buses found connected to the BMC\n";
814 return @nodes;
815 }
816
817 foreach my $i2c (@{$connections->{CONN}}) {
818
819 my %deviceNode, my $deviceName;
820
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500821 $deviceNode{COMMENT} = connectionComment($i2c);
Matt Spinler74909132016-10-07 13:52:19 -0500822
823 $deviceName = lc $i2c->{DEST_PARENT};
824 $deviceName =~ s/-\d+$//; #remove trailing position
825 $deviceName =~ s/.*\///; #remove the front of the path
826
827 #Get the I2C address
828 my $i2cAddress = $g_targetObj->getAttribute($i2c->{DEST}, "I2C_ADDRESS");
829 $i2cAddress = hex($i2cAddress);
830 if ($i2cAddress == 0) {
831 die "ERROR: Missing I2C address on $i2c->{DEST}\n";
832 }
833
834 #Put it in the format we want to print it in
835 $i2cAddress = adjustI2CAddress($i2cAddress);
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600836 addRegProp(\%deviceNode, $i2cAddress);
Matt Spinler74909132016-10-07 13:52:19 -0500837
838 $deviceName = makeNodeName($deviceName, $deviceNode{reg});
839
840 #Get the I2C bus number
841 if ($g_targetObj->isBadAttribute($i2c->{SOURCE},
842 "I2C_PORT")) {
843 die "ERROR: I2C_PORT attribute in $i2c->{DEST_PARENT} " .
844 "is either missing or empty.\n";
845 }
846
847 my $busNum = $g_targetObj->getAttribute($i2c->{SOURCE}, "I2C_PORT");
848 if ($busNum =~ /0x/i) {
849 $busNum = hex($busNum);
850 }
851
852 #Convert the number to the Linux numbering scheme.
Matt Spinler30b461c2016-10-10 16:50:07 -0500853 $busNum += $g_i2cBusAdjust;
Matt Spinler74909132016-10-07 13:52:19 -0500854
855 #Get the compatible property
856 if ($g_targetObj->isBadAttribute($i2c->{DEST_PARENT},
857 "BMC_DT_COMPATIBLE")) {
858 die "ERROR: BMC_DT_COMPATIBLE attribute in $i2c->{DEST_PARENT} " .
859 "is either missing or empty.\n";
860 }
861
862 $deviceNode{compatible} = $g_targetObj->getAttribute(
863 $i2c->{DEST_PARENT},
864 "BMC_DT_COMPATIBLE");
865
866 #Get any other part specific properties, where the property
867 #names are actually defined in the XML.
868 my %props = getPartDefinedDTProperties($i2c->{DEST_PARENT});
869 foreach my $prop (sort keys %props) {
870 $deviceNode{$prop} = $props{$prop};
871 }
872
873 #busNodeName is the hash twice so when we loop
874 #below it doesn't get lost
875 my $busNodeName = "i2c$busNum";
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600876 statusOK(\%{$busNodes{$busNodeName}{$busNodeName}});
Matt Spinler74909132016-10-07 13:52:19 -0500877 $busNodes{$busNodeName}{$busNodeName}{$deviceName} = { %deviceNode };
Matt Spinler2efdcba2016-11-08 15:37:20 -0600878
879 #Add in any pinctrl properties. These would come from the parent
880 #of $i2c{SOURCE}, which would be a unit-pingroup-bmc if the
881 #pins for this connection are multi-function.
882 addPinCtrlProps($g_targetObj->getTargetParent($i2c->{SOURCE}),
883 \%{$busNodes{$busNodeName}{$busNodeName}});
Matt Spinler74909132016-10-07 13:52:19 -0500884 }
885
886 #Each bus gets its own hash entry in the array
887 for my $b (sort keys %busNodes) {
888 push @nodes, { %{$busNodes{$b}} };
889 }
890
891 return @nodes;
892}
893
894
895#Returns a hash of property names and values that should be stored in
896#the device tree node for this device. The names of the properties and
897#the attributes to find their values in are stored in the
898#BMC_DT_ATTR_NAMES attribute in the chip.
899# $chip = the chip target
Matt Spinler889343f2017-01-30 14:14:11 -0600900sub getPartDefinedDTProperties
Matt Spinler74909132016-10-07 13:52:19 -0500901{
902 my $chip = shift;
903 my %props;
904
905 if ($g_targetObj->isBadAttribute($chip, "BMC_DT_ATTR_NAMES")) {
906 return %props;
907 }
908
909 my $attr = $g_targetObj->getAttribute($chip, "BMC_DT_ATTR_NAMES");
910 $attr =~ s/\s//g;
911 my @names = split(',', $attr);
912
913 #There can be up to 4 entries in this attribute
914 for (my $i = 0; $i < scalar @names; $i += 2) {
915
916 #$names[$i] holds the name of the attribute.
917 #$names[$i+1] holds the name of the property to store its value in.
918 if (($names[$i] ne "NA") && ($names[$i] ne "")) {
919
920 my $val = $g_targetObj->getAttribute($chip, $names[$i]);
921
922 #if the value is empty, assume it's for a standalone property,
923 #which gets turned into: some-property;
924 if ($val eq "") {
925 $props{$names[$i+1]} = ZERO_LENGTH_PROPERTY;
926 }
927 else {
928 $props{$names[$i+1]} = "<$val>";
929 }
930 }
931 }
932
933 return %props;
934}
935
936
937#Convert the MRW I2C address into the format the dts needs
938# $addr = the I2C Address
Matt Spinler889343f2017-01-30 14:14:11 -0600939sub adjustI2CAddress
Matt Spinler74909132016-10-07 13:52:19 -0500940{
941 my $addr = shift;
942
943 #MRW holds the 8 bit value. We need the 7 bit one.
Matt Spinler96f8f242016-11-28 16:26:57 -0600944 $addr = $addr >> 1;
Matt Spinler74909132016-10-07 13:52:19 -0500945 $addr = sprintf("0x%X", $addr);
946 $addr = lc $addr;
947
948 return $addr;
949}
950
951
Matt Spinler30b461c2016-10-10 16:50:07 -0500952#Sets the global $g_i2cBusAdjust from the configuration file.
Matt Spinler889343f2017-01-30 14:14:11 -0600953sub getI2CBusAdjust
Matt Spinler74909132016-10-07 13:52:19 -0500954{
Matt Spinler30b461c2016-10-10 16:50:07 -0500955 if (exists $g_configuration{"i2c-bus-adjust"}) {
Matt Spinler74909132016-10-07 13:52:19 -0500956
Matt Spinler30b461c2016-10-10 16:50:07 -0500957 $g_i2cBusAdjust = $g_configuration{"i2c-bus-adjust"};
Matt Spinler74909132016-10-07 13:52:19 -0500958
Matt Spinler30b461c2016-10-10 16:50:07 -0500959 if (!looks_like_number($g_i2cBusAdjust)) {
960 die "ERROR: Invalid i2c-bus-adjust value $g_i2cBusAdjust " .
961 "found in config file.\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500962 }
963 }
Matt Spinler30b461c2016-10-10 16:50:07 -0500964 else {
965 $g_i2cBusAdjust = 0;
966 print "WARNING: No I2C Bus number adjustment done " .
967 "for this system.\n";
968 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500969}
970
971
Matt Spinler2efdcba2016-11-08 15:37:20 -0600972
973#Adds two pinctrl properties to the device node hash passed in,
974#if specified in the MRW. Pin Control refers to a mechanism for
975#Linux to know which function of a multi-function pin to configure.
976#For example, a pin could either be configured to be a GPIO, or
977#an I2C clock line. The pin function depends on board wiring,
978#so is known by the MRW.
979# $target = the target to get the BMC_DT_PINCTRL_FUNCTS attribute from
980# $node = a hash reference to the device tree node to add the properties to
Matt Spinler889343f2017-01-30 14:14:11 -0600981sub addPinCtrlProps
Matt Spinler2efdcba2016-11-08 15:37:20 -0600982{
983 my ($target, $node) = @_;
984
985 if (!$g_targetObj->isBadAttribute($target, "BMC_DT_PINCTRL_FUNCS")) {
986 my $attr = $g_targetObj->getAttribute($target,
987 "BMC_DT_PINCTRL_FUNCS");
988
989 my $pinCtrl0Prop = makePinCtrl0PropValue($attr);
990 if ($pinCtrl0Prop ne "") {
991 $node->{"pinctrl-names"} = "default";
992 $node->{"pinctrl-0"} = $pinCtrl0Prop;
993 }
994 }
995}
996
997
998#Constructs the pinctrl-0 property value based on the
999#BMC_DT_PINCTRL_FUNCS attribute passed in.
1000# $attr = BMC_DT_PINCTRL_FUNCS attribute value, which is an array
Matt Spinler889343f2017-01-30 14:14:11 -06001001sub makePinCtrl0PropValue
Matt Spinler2efdcba2016-11-08 15:37:20 -06001002{
1003 my $attr = shift;
1004 my @entries;
1005 my $value = "";
1006
1007 $attr =~ s/\s//g;
1008 my @funcs = split(',', $attr);
1009 foreach my $func (@funcs) {
1010 if (($func ne "NA") && ($func ne "")) {
1011 push @entries, $func;
1012 }
1013 }
1014
1015 #<&pinctrl_funcA_default &pinctrl_funcB_default ...>
1016 if (scalar @entries) {
1017 $value = "<";
1018 foreach my $entry (@entries) {
1019 $value .= "&pinctrl_".$entry."_default ";
1020 }
1021 $value =~ s/\s$//; #Remove the trailing space
1022 $value .= ">";
1023 }
1024
1025 return $value;
1026}
1027
1028
Matt Spinler7d381e12016-09-27 14:27:24 -05001029#Returns a list of compatible fields for the BMC itself.
Matt Spinler889343f2017-01-30 14:14:11 -06001030sub getBMCCompatibles
Matt Spinler7d381e12016-09-27 14:27:24 -05001031{
1032 my @compats;
1033
Matt Spinler23d47c22016-10-04 12:31:21 -05001034 #1st entry: <system mfgr>,<system name>-bmc
1035 #2nd entry: <bmc mfgr>,<bmc model>
Matt Spinler7d381e12016-09-27 14:27:24 -05001036
Matt Spinler23d47c22016-10-04 12:31:21 -05001037 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() }) {
1038 if ($g_targetObj->getType($target) eq "SYS") {
1039 my $mfgr = $g_targetObj->getAttribute($target, "MANUFACTURER");
1040 push @compats, lc "$mfgr,$g_systemName-bmc";
1041 last;
1042 }
Matt Spinler7d381e12016-09-27 14:27:24 -05001043 }
1044
1045 push @compats, lc($g_bmcMfgr).",".lc($g_bmcModel);
1046
1047 return @compats;
1048}
1049
1050
1051#Returns a string for the system's BMC model property
Matt Spinler889343f2017-01-30 14:14:11 -06001052sub getSystemBMCModel
Matt Spinler7d381e12016-09-27 14:27:24 -05001053{
Matt Spinler995f2a22016-09-30 13:07:31 -05001054 #'<System> BMC'
Matt Spinler7d381e12016-09-27 14:27:24 -05001055 my $sys = lc $g_systemName;
1056 $sys = uc(substr($sys, 0, 1)) . substr($sys, 1);
1057
1058 return $sys . " BMC";
1059}
1060
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001061#Create the comment that will show up in the device tree
1062#for a connection. In the output, will look like:
1063# // sourceUnit ->
1064# // destChip
1065#
1066# $conn = The connection hash reference
Matt Spinler889343f2017-01-30 14:14:11 -06001067sub connectionComment
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001068{
1069 my $conn = shift;
1070 my $comment = "$conn->{SOURCE} ->\n$conn->{DEST_PARENT}";
1071 return $comment;
1072}
1073
Matt Spinler7d381e12016-09-27 14:27:24 -05001074
1075#Prints a list of nodes at the same indent level
1076# $f = file handle
1077# $level = indent level (0,1,etc)
1078# @nodes = array of node hashes to print, where the
1079# key for the hash is the name of the node
Matt Spinler889343f2017-01-30 14:14:11 -06001080sub printNodes
Matt Spinler7d381e12016-09-27 14:27:24 -05001081{
1082 my ($f, $level, @nodes) = @_;
1083
1084 foreach my $n (@nodes) {
1085 my %node = %$n;
1086
1087 foreach my $name (sort keys %node) {
1088 my %n = %{ $node{$name} };
1089 printNode($f, $level, $name, %n);
1090 }
1091 }
1092}
1093
1094
1095#Print a single node and its children
1096# $f = file handle
1097# $level = indent level (0,1,etc)
1098# $name = the name of the node - shows up as:
1099# name { ...
1100# %vals = The contents of the node, with the following options:
1101# if the key is:
1102# - 'DTSI_INCLUDE', then value gets turned into a #include
Matt Spinler995f2a22016-09-30 13:07:31 -05001103# - 'COMMENT', then value gets turned into a // comment
Matt Spinler74909132016-10-07 13:52:19 -05001104# - 'ZERO_LENGTH_PROPERTY' then value gets turned into: value;
Matt Spinler7d381e12016-09-27 14:27:24 -05001105#
1106# If the value is:
1107# - a hash - then that hash gets turned into a child node
1108# where the key is the name of the child node
Matt Spinler995f2a22016-09-30 13:07:31 -05001109# - an array of hashes indicates an array of child nodes
Matt Spinler889343f2017-01-30 14:14:11 -06001110sub printNode
Matt Spinler7d381e12016-09-27 14:27:24 -05001111{
1112 my ($f, $level, $name, %vals) = @_;
1113 my $include = "";
1114
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001115 #No reason to print an empty node
1116 if (!keys %vals) {
1117 return;
1118 }
1119
Matt Spinler7d381e12016-09-27 14:27:24 -05001120 if ($level == 0) {
1121 $name = "&".$name;
1122 }
1123
Matt Spinler995f2a22016-09-30 13:07:31 -05001124 print $f "\n";
1125
1126 if (exists $vals{COMMENT}) {
1127 my @lines = split('\n', $vals{COMMENT});
1128 foreach my $l (@lines) {
1129 print $f indent($level) . "// $l\n";
1130 }
1131 }
1132
Matt Spinler6d391252017-01-31 13:46:06 -06001133 #The node can have a label, which looks like:
1134 #label : name {
1135 my $label = "";
1136 if (exists $vals{NODE_LABEL}) {
1137 $label = $vals{NODE_LABEL} . ": ";
1138 }
1139
1140 print $f indent($level) . $label . "$name {\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001141
Matt Spinler74909132016-10-07 13:52:19 -05001142 #First print properties, then includes, then subnodes
1143
1144 #Print Properties
Matt Spinler7d381e12016-09-27 14:27:24 -05001145 foreach my $v (sort keys %vals) {
1146
Matt Spinler995f2a22016-09-30 13:07:31 -05001147 next if ($v eq "COMMENT");
Matt Spinler74909132016-10-07 13:52:19 -05001148 next if ($v eq "DTSI_INCLUDE");
Matt Spinler6d391252017-01-31 13:46:06 -06001149 next if ($v eq "NODE_LABEL");
Matt Spinler74909132016-10-07 13:52:19 -05001150 next if (ref($vals{$v}) eq "HASH");
1151 next if (ref($vals{$v}) eq "ARRAY");
Matt Spinler995f2a22016-09-30 13:07:31 -05001152
Matt Spinler74909132016-10-07 13:52:19 -05001153 if ($vals{$v} ne ZERO_LENGTH_PROPERTY) {
1154 printProperty($f, $level+1, $v, $vals{$v});
Matt Spinler7d381e12016-09-27 14:27:24 -05001155 }
Matt Spinler74909132016-10-07 13:52:19 -05001156 else {
1157 printZeroLengthProperty($f, $level+1, $v);
1158 }
1159 }
1160
1161 #Print Includes
1162 foreach my $v (sort keys %vals) {
1163
1164 if ($v eq "DTSI_INCLUDE") {
1165 #print 1 include per line
1166 my @incs = split(',', $vals{$v});
1167 foreach my $i (@incs) {
1168 print $f qq(#include "$i";\n);
1169 }
1170 }
1171 }
1172
1173 #Print Nodes
1174 foreach my $v (sort keys %vals) {
1175
1176 if (ref($vals{$v}) eq "HASH") {
Matt Spinler7d381e12016-09-27 14:27:24 -05001177 printNode($f, $level+1, $v, %{$vals{$v}});
1178 }
Matt Spinler995f2a22016-09-30 13:07:31 -05001179 #An array of nested nodes
1180 elsif (ref($vals{$v}) eq "ARRAY") {
1181 my @array = @{$vals{$v}};
1182 &printNodes($f, $level+1, @array);
1183 }
Matt Spinler7d381e12016-09-27 14:27:24 -05001184 }
1185
1186 print $f indent($level) . "};\n";
1187}
1188
1189
1190#Prints a comma separated list of properties.
1191#e.g. a = "b, c, d";
1192# $f = file handle
1193# $level = indent level (0,1,etc)
1194# $name = name of property
1195# @vals = list of property values
Matt Spinler889343f2017-01-30 14:14:11 -06001196sub printPropertyList
Matt Spinler7d381e12016-09-27 14:27:24 -05001197{
1198 my ($f, $level, $name, @vals) = @_;
1199
1200 print $f indent($level) . "$name = ";
1201
1202 for (my $i = 0;$i < scalar @vals; $i++) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001203 print $f qq("$vals[$i]");
Matt Spinler7d381e12016-09-27 14:27:24 -05001204 if ($i < (scalar(@vals) - 1)) {
1205 print $f ", ";
1206 }
1207 }
1208 print $f ";\n"
1209}
1210
1211
1212#Prints a single property. e.g. a = "b";
1213# $f = file handle
1214# $level = indent level (0,1,etc)
1215# $name = name of property
1216# @vals = property values
Matt Spinler889343f2017-01-30 14:14:11 -06001217sub printProperty
Matt Spinler7d381e12016-09-27 14:27:24 -05001218{
1219 my ($f, $level, $name, $val) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001220 my $quoteChar = qq(");
Matt Spinler23d47c22016-10-04 12:31:21 -05001221
Matt Spinler30b461c2016-10-10 16:50:07 -05001222 $val = convertReference($val);
Matt Spinler23d47c22016-10-04 12:31:21 -05001223
1224 #properties with < > or single word aliases don't need quotes
1225 if (($val =~ /<.*>/) || ($val =~ /^&\w+$/)) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001226 $quoteChar = "";
Matt Spinler23d47c22016-10-04 12:31:21 -05001227 }
1228
Matt Spinler30b461c2016-10-10 16:50:07 -05001229 print $f indent($level) . "$name = $quoteChar$val$quoteChar;\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001230}
1231
1232
Matt Spinler30b461c2016-10-10 16:50:07 -05001233#Prints a zero length property e.g. some-property;
Matt Spinler7d381e12016-09-27 14:27:24 -05001234# $f = file handle
1235# $level = indent level (0,1,etc)
1236# $name = name of property
Matt Spinler889343f2017-01-30 14:14:11 -06001237sub printZeroLengthProperty
Matt Spinler7d381e12016-09-27 14:27:24 -05001238{
1239 my ($f, $level, $name) = @_;
1240 print $f indent($level) . "$name;\n";
1241}
1242
1243
Matt Spinler30b461c2016-10-10 16:50:07 -05001244#Replace '(ref)' with '&'.
Matt Spinler7d381e12016-09-27 14:27:24 -05001245#Needed because Serverwiz doesn't properly escape '&'s in the XML,
Matt Spinler30b461c2016-10-10 16:50:07 -05001246#so the '(ref)' string is used to represent the reference
Matt Spinler7d381e12016-09-27 14:27:24 -05001247#specifier instead of '&'.
Matt Spinler889343f2017-01-30 14:14:11 -06001248sub convertReference
1249{
Matt Spinler7d381e12016-09-27 14:27:24 -05001250 my $val = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -05001251 $val =~ s/\(ref\)/&/g;
Matt Spinler7d381e12016-09-27 14:27:24 -05001252 return $val
1253}
1254
1255
Matt Spinler7d381e12016-09-27 14:27:24 -05001256#Prints the device tree version line.
1257# $f = file handle
Matt Spinler889343f2017-01-30 14:14:11 -06001258sub printVersion
Matt Spinler7d381e12016-09-27 14:27:24 -05001259{
1260 my $f = shift;
1261 print $f VERSION."\n"
1262}
1263
1264
1265#Prints the #include line for pulling in an include file.
Matt Spinler30b461c2016-10-10 16:50:07 -05001266#The files to include come from the configuration file.
Matt Spinler7d381e12016-09-27 14:27:24 -05001267# $f = file handle
Matt Spinler30b461c2016-10-10 16:50:07 -05001268# $type = include type
Matt Spinler889343f2017-01-30 14:14:11 -06001269sub printIncludes
Matt Spinler7d381e12016-09-27 14:27:24 -05001270{
Matt Spinler30b461c2016-10-10 16:50:07 -05001271 my ($f, $type) = @_;
1272 my @includes = getIncludes($type);
Matt Spinler7d381e12016-09-27 14:27:24 -05001273
1274 foreach my $i (@includes) {
1275 #if a .dtsi, gets " ", otherwise < >
1276 if ($i =~ /\.dtsi$/) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001277 $i = qq("$i");
Matt Spinler7d381e12016-09-27 14:27:24 -05001278 }
1279 else {
Matt Spinler30b461c2016-10-10 16:50:07 -05001280 $i = "<$i>";
Matt Spinler7d381e12016-09-27 14:27:24 -05001281 }
Matt Spinler30b461c2016-10-10 16:50:07 -05001282 print $f "#include $i\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001283 }
1284}
1285
1286
Matt Spinler30b461c2016-10-10 16:50:07 -05001287#Returns an array of include files found in the config file
1288#for the type specified.
1289# $type = the include type, which is the section name in the
1290# YAML configuration file.
Matt Spinler889343f2017-01-30 14:14:11 -06001291sub getIncludes
Matt Spinler7d381e12016-09-27 14:27:24 -05001292{
Matt Spinler30b461c2016-10-10 16:50:07 -05001293 my $type = shift;
Matt Spinler7d381e12016-09-27 14:27:24 -05001294 my @includes;
1295
Matt Spinler30b461c2016-10-10 16:50:07 -05001296 #The config file may have a section but no includes
1297 #listed in it, which is OK.
1298 if ((exists $g_configuration{includes}{$type}) &&
1299 (ref($g_configuration{includes}{$type}) eq "ARRAY")) {
Matt Spinler7d381e12016-09-27 14:27:24 -05001300
Matt Spinler30b461c2016-10-10 16:50:07 -05001301 @includes = @{$g_configuration{includes}{$type}};
Matt Spinler7d381e12016-09-27 14:27:24 -05001302 }
1303
1304 return @includes;
1305}
1306
Matt Spinler30b461c2016-10-10 16:50:07 -05001307
Matt Spinler74909132016-10-07 13:52:19 -05001308#Appends the first value of the 'reg' property
1309#passed in to the name passed in to create the
1310#full name for the node
1311# $name = node name that will be appended to
1312# $reg = the reg property values
Matt Spinler889343f2017-01-30 14:14:11 -06001313sub makeNodeName
Matt Spinler74909132016-10-07 13:52:19 -05001314{
1315 my ($name, $reg) = @_;
1316
1317 $reg =~ s/<//g;
1318 $reg =~ s/>//g;
1319 my @vals = split(' ', $reg);
1320
1321 if (scalar @vals > 0) {
1322 $vals[0] =~ s/0x//;
1323 $name .= "@" . lc $vals[0];
1324 }
1325
1326 return $name;
1327}
1328
Matt Spinler7d381e12016-09-27 14:27:24 -05001329
1330#Prints the root node starting bracket.
1331# $f = file handle
Matt Spinler889343f2017-01-30 14:14:11 -06001332sub printRootNodeStart
1333{
Matt Spinler7d381e12016-09-27 14:27:24 -05001334 my $f = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -05001335 print $f qq(/ {\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001336}
1337
1338
1339#Prints the root node ending bracket.
1340# $f = file handle
1341# $level = indent level (0,1,etc)
Matt Spinler889343f2017-01-30 14:14:11 -06001342sub printRootNodeEnd
1343{
Matt Spinler7d381e12016-09-27 14:27:24 -05001344 my ($f, $level) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001345 print $f indent($level).qq(};\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001346}
1347
1348
1349#Returns a string that can be used to indent based on the
1350#level passed in. Each level is an additional 4 spaces.
1351# $level = indent level (0,1,etc)
Matt Spinler889343f2017-01-30 14:14:11 -06001352sub indent
1353{
Matt Spinler7d381e12016-09-27 14:27:24 -05001354 my $level = shift;
1355 return ' ' x ($level * 4);
1356}
1357
1358
Matt Spinler9ac5cbe2017-01-31 14:33:25 -06001359#Adds a {status} = "okay" element to the hash passed in.
1360# $node = reference to the hash to add element to
1361sub statusOK
1362{
1363 my $node = shift;
1364 $node->{status} = "okay";
1365}
1366
1367
1368#Adds the {reg} element to the hash passed in using the values
1369#passed in. Resulting value looks like: "<val1 val2 etc>"
1370# $node = reference to the hash to add element to
1371# @values = the values for the property. May be passed in one at
1372# a time and not as an array.
1373sub addRegProp
1374{
1375 my $node = shift;
1376 my @values = @_;
1377
1378 $node->{reg} = "<";
1379 for (my $i = 0; $i < scalar @values; $i++) {
1380 $node->{reg} .= $values[$i];
1381 if ($i < (scalar @values) - 1) {
1382 $node->{reg} .= " ";
1383 }
1384 }
1385 $node->{reg} .= ">";
1386}
1387
1388
Matt Spinler7d381e12016-09-27 14:27:24 -05001389sub printUsage
1390{
Matt Spinler30b461c2016-10-10 16:50:07 -05001391 print "gen_devtree.pl -x [XML filename] -y [yaml config file] " .
1392 "-o [output filename]\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001393 exit(1);
1394}