blob: 2b582037c7bf2c904c4d4d0a473f112e01dce8b7 [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
208 $memory{reg} = "<$g_configuration{memory}{base} " .
209 "$g_configuration{memory}{size}>";
210
211 return %memory;
212}
213
214
Matt Spinler6d391252017-01-31 13:46:06 -0600215#Returns a hash that represents the 'reserved-memory' node.
216#This currently only supports the memory region for the LPC
217#host spi flash mailbox. Will look like:
218# reserved-memory {
219# #address-cells = <1>;
220# #size-cells = <1>;
221# ranges;
222#
223# flash_memory: region@94000000 {
224# no-map;
225# reg = <0x94000000 0x04000000>;
226# };
227# };
228sub getReservedMemory
229{
230 my %memory;
231
232 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
233 return %memory;
234 }
235
236 $memory{"#address-cells"} = "<1>";
237 $memory{"#size-cells"} = "<1>";
238 $memory{ranges} = ZERO_LENGTH_PROPERTY;
239
240 #Get the sub node that contains the address range
241 my ($name, $node) = getHostSpiFlashMboxRegion();
242 $memory{$name} = { %$node };
243
244 return %memory;
245}
246
247
248#Returns a hash that represents a child node of the
249#reserved-memory node which contains the address range
250#that the host spi flash is mapped to.
251sub getHostSpiFlashMboxRegion
252{
253 my %node;
254
255 $node{"no-map"} = ZERO_LENGTH_PROPERTY;
256
257 #This node needs a label the LPC node can refer to.
258 $node{NODE_LABEL} = HOST_SPI_FLASH_MEM_REGION_NODE_LABEL;
259
260 #Get the memory region's base address and size from the config file
261 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}
262 {"bmc-address-range"}{base}) {
263 die "Could not find lpc-host-spi-flash-mailbox base " .
264 "address in config file\n";
265 }
266
267 my $base = $g_configuration{"lpc-host-spi-flash-mailbox"}
268 {"bmc-address-range"}{base};
269 #Allow 1 hex value, up to 4B
270 if ($base !~ /^0x[0-9a-fA-F]{1,8}$/) {
271 die "lpc-host-spi-flash-mailbox base address $base is invalid\n";
272 }
273
274 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}
275 {"bmc-address-range"}{size}) {
276 die "Could not find lpc-host-spi-flash-mailbox address size " .
277 "in config file\n";
278 }
279
280 my $size = $g_configuration{"lpc-host-spi-flash-mailbox"}
281 {"bmc-address-range"}{size};
282 if ($size !~ /^0x[0-9a-fA-F]{1,8}$/) {
283 die "lpc-host-spi-flash-mailbox address range size " .
284 "$size is invalid\n";
285 }
286
287 $node{"reg"} = "<$base $size>";
288 my $name = makeNodeName("region", $node{reg});
289
290 return ($name, \%node);
291}
292
293
Matt Spinler25d60bb2016-10-31 15:16:03 -0500294#Returns an array of hashes representing the device tree nodes for
295#the BMC flash. These nodes are BMC model specific because different
296#models can have different device drivers.
Matt Spinler889343f2017-01-30 14:14:11 -0600297sub getBMCFlashNodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500298{
Matt Spinler25d60bb2016-10-31 15:16:03 -0500299 my @nodes;
300
301 if ($g_bmcModel eq "AST2500") {
302 my %node = getAST2500BMCSPIFlashNode();
303 push @nodes, { %node };
304 }
305 else {
306 die "ERROR: No BMC SPI flash support yet for BMC model $g_bmcModel\n";
307 }
308
309 return @nodes;
310}
311
312
313#Returns a hash that represents the BMC SPI flash(es) by finding the SPI
314#connections that come from the unit tagged as BMC_CODE. The code also
315#looks in the config file for any additional properties to add. Supports
316#the hardware where the same SPI master unit can be wired to more than 1
317#flash (a chip select line is used to switch between them.) This is
318#specific to the ASPEED AST2500 hardware and device driver.
319#Will look like:
320# fmc {
321# status = "okay"
322# flash@0 {
323# ...
324# };
325# flash@1 {
326# ...
327# };
Matt Spinler889343f2017-01-30 14:14:11 -0600328sub getAST2500BMCSPIFlashNode
Matt Spinler25d60bb2016-10-31 15:16:03 -0500329{
330 my %bmcFlash;
331 my $chipSelect = 0;
332 my $lastUnit = "";
333
Matt Spinler18d5f572016-11-15 15:25:45 -0600334 my $connections = $g_targetObj->findConnections($g_bmc, "SPI", "FLASH");
Matt Spinler25d60bb2016-10-31 15:16:03 -0500335
336 if ($connections eq "") {
337 die "ERROR: No BMC SPI flashes found connected to the BMC\n";
338 }
339
340 $bmcFlash{fmc}{status} = "okay";
341
342 foreach my $spi (@{$connections->{CONN}}) {
343
344 #Looking for spi-masters with a function of 'BMC_CODE'.
345 #It's possible there are multiple flash chips here.
346 if (!$g_targetObj->isBadAttribute($spi->{SOURCE}, "SPI_FUNCTION")) {
347
348 my $function = $g_targetObj->getAttribute($spi->{SOURCE},
349 "SPI_FUNCTION");
350 if ($function eq "BMC_CODE") {
351
352 my $flashName = "flash@".$chipSelect;
353
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500354 $bmcFlash{fmc}{$flashName}{COMMENT} = connectionComment($spi);
Matt Spinler25d60bb2016-10-31 15:16:03 -0500355
356 $bmcFlash{fmc}{$flashName}{status} = "okay";
357
358 #Add in anything specified in the config file for this chip.
359 addBMCFlashConfigProperties(\%{$bmcFlash{fmc}{$flashName}},
360 $chipSelect);
361
362 #The code currently only supports the config where a chip
363 #select line is used to select between possibly multiple
364 #flash chips attached to the same SPI pins/unit. So we
365 #need to make sure if there are multiple chips found, that
366 #they are off of the same master unit.
367 if ($lastUnit eq "") {
368 $lastUnit = $spi->{SOURCE};
369 }
370 else {
371 if ($lastUnit ne $spi->{SOURCE}) {
372 die "ERROR: Currently only 1 spi-master unit is " .
373 "supported for BMC flash connections."
374 }
375 }
376
377 #Since we don't need anything chip select specific from the
378 #XML, we can just assign our own chip selects.
379 $chipSelect++;
380 }
381 }
382 }
383
384 if ($chipSelect == 0) {
385 die "ERROR: Didn't find any BMC flash chips connected";
386 }
387
388 return %bmcFlash;
389}
390
391
392#Looks in the bmc-flash-config section in the config file for the
393#chip select passed in to add any additional properties to the BMC
394#flash node.
395# $node = hash reference to the flash node
396# $cs = the flash chip select value
Matt Spinler889343f2017-01-30 14:14:11 -0600397sub addBMCFlashConfigProperties
Matt Spinler25d60bb2016-10-31 15:16:03 -0500398{
399 my ($node, $cs) = @_;
400 my $section = "chip-select-$cs";
401
402 if (exists $g_configuration{"bmc-flash-config"}{$section}) {
403 foreach my $key (sort keys $g_configuration{"bmc-flash-config"}{$section}) {
404 $node->{$key} = $g_configuration{"bmc-flash-config"}{$section}{$key};
405 }
406 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500407}
408
409
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500410#Returns an array of hashes representing the other flashes used by the
411#BMC besides the ones that hold the BMC code. This is BMC model specific
412#as different models can have different interfaces.
413#Typically, these are SPI flashes.
Matt Spinler889343f2017-01-30 14:14:11 -0600414sub getOtherFlashNodes
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500415{
416 my @nodes;
417
418 if ($g_bmcModel eq "AST2500") {
419 @nodes = getAST2500SpiFlashNodes();
420 }
421 else {
422 die "ERROR: No SPI flash support yet for BMC model $g_bmcModel\n";
423 }
424
425 return @nodes;
426}
427
428
429#Returns an array of hashes representing the SPI flashes in an
430#AST2500. These are for the SPI1 and SPI2 interfaces in the chip.
431#Each SPI master interface can support multiple flash chips. If
432#no hardware is connected to the interface, the node won't be present.
Matt Spinler889343f2017-01-30 14:14:11 -0600433sub getAST2500SpiFlashNodes
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500434{
435 my @nodes;
436
437 #The AST2500 has 2 SPI master units, 1 and 2.
438 my @units = (1, 2);
439
440 foreach my $unit (@units) {
441
Matt Spinler908e1822017-01-31 14:02:58 -0600442 my ($node, $foundPNOR) = getAST2500SpiMasterNode($unit);
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500443
Matt Spinler908e1822017-01-31 14:02:58 -0600444 if (keys %$node) {
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500445 my %spiNode;
446 my $nodeName = "spi$unit";
Matt Spinler908e1822017-01-31 14:02:58 -0600447 $spiNode{$nodeName} = { %$node };
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500448 push @nodes, { %spiNode };
Matt Spinler908e1822017-01-31 14:02:58 -0600449
450 #Save off the PNOR SPI node name for use by LPC node
451 if ($foundPNOR) {
452 $g_pnorNodeName = $nodeName;
453 }
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500454 }
455 }
456
457 return @nodes;
458}
459
460
461#Returns a hash that represents the device tree node for the SPI1
462#or SPI2 master interface on the AST2500. Each master can support
463#multiple chips by use of a chip select.
464#Will look like:
465# spi1 {
466# status = "okay";
467# flash@0 {
468# ...
469# };
470# };
471#
472# $spiNum = The SPI master unit number to use
Matt Spinler889343f2017-01-30 14:14:11 -0600473sub getAST2500SpiMasterNode
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500474{
475 my $spiNum = shift;
476 my %spiMaster;
477 my $chipSelect = 0;
Matt Spinler908e1822017-01-31 14:02:58 -0600478 my $foundPNOR = 0;
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500479
Matt Spinler18d5f572016-11-15 15:25:45 -0600480 my $connections = $g_targetObj->findConnections($g_bmc, "SPI", "FLASH");
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500481
482 if ($connections eq "") {
483 return %spiMaster;
484 }
485
486 #Looking for spi-masters with a chip-unit of $spiNum
487 #It's possible there are multiple flash chips off the master
488 foreach my $spi (@{$connections->{CONN}}) {
489
490 my $unitNum = $g_targetObj->getAttribute($spi->{SOURCE},
491 "CHIP_UNIT");
492 if ($unitNum == $spiNum) {
493 $spiMaster{status} = "okay";
Matt Spinler2efdcba2016-11-08 15:37:20 -0600494
495 #Add in any pinctrl properties. These would come from the parent
496 #of $spi{SOURCE}, which would be a unit-pingroup-bmc if the
497 #pins for this connection are multi-function.
498 addPinCtrlProps($g_targetObj->getTargetParent($spi->{SOURCE}),
499 \%spiMaster);
500
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500501 my $flashName = "flash@".$chipSelect;
502
503 $spiMaster{$flashName}{COMMENT} = connectionComment($spi);
504
505 $spiMaster{$flashName}{status} = "okay";
506
Matt Spinler0eda4882016-11-30 15:20:11 -0600507 #AST2500 PNORs need a label
508 my $function = $g_targetObj->getAttribute($spi->{SOURCE},
509 "SPI_FUNCTION");
510 if ($function eq "PNOR") {
511 $spiMaster{$flashName}{label} = "pnor";
Matt Spinler908e1822017-01-31 14:02:58 -0600512 $foundPNOR = 1;
Matt Spinler0eda4882016-11-30 15:20:11 -0600513 }
514
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500515 $chipSelect++;
516 }
517 }
518
Matt Spinler908e1822017-01-31 14:02:58 -0600519 return (\%spiMaster, $foundPNOR);
520}
521
522
523#Returns a hash that represents the mbox node.
524#This node is used by the LPC mailbox device driver.
525#Only present if the LPC mailbox is enabled in the config file.
526#Node looks like:
527# &mbox {
528# status = "okay";
529# }
530sub getMBoxNode
531{
532 my %node;
533 if (exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
534 $node{status} = "okay";
535 }
536
537 return %node;
538}
539
540
541#Returns a hash that represents the LPC node.
542#Only present if the LPC mailbox is enabled in the config file.
543#Node looks like:
544# &lpc_ctrl {
545# flash = <&spi1>;
546# memory-region = <&flash_memory>;
547# status = "okay";
548#};
549sub getLPCNode
550{
551 my %node;
552 if (exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
553
554 $node{status} = "okay";
555
556 #Point to the reserved-memory region label
557 $node{"memory-region"} = "<(ref)" .
558 HOST_SPI_FLASH_MEM_REGION_NODE_LABEL . ">";
559
560 if (not defined $g_pnorNodeName) {
561 die "The PNOR SPI flash node cannot be found but is required " .
562 "if the LPC mailbox is enabled.\n";
563 }
564
565 $node{flash} = "<(ref)$g_pnorNodeName>";
566 }
567
568 return %node;
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500569}
570
571
Matt Spinler995f2a22016-09-30 13:07:31 -0500572#Returns a hash that represents the leds node by finding all of the
573#GPIO connections to LEDs.
574#Node will look like:
575# leds {
576# <ledname> {
577# gpios = &gpio ASPEED_GPIO(x, y) GPIO_ACTIVE_xxx>
578# };
579# <another ledname> {
580# ...
581# }
Matt Spinler889343f2017-01-30 14:14:11 -0600582sub getLEDNode
Matt Spinler995f2a22016-09-30 13:07:31 -0500583{
584 my %leds;
585
Matt Spinlereca7f062016-11-07 09:59:23 -0600586 $leds{compatible} = "gpio-leds";
Matt Spinler995f2a22016-09-30 13:07:31 -0500587
Matt Spinler18d5f572016-11-15 15:25:45 -0600588 my $connections = $g_targetObj->findConnections($g_bmc, "GPIO", "LED");
Matt Spinler995f2a22016-09-30 13:07:31 -0500589
590 if ($connections eq "") {
591 print "WARNING: No LEDs found connected to the BMC\n";
592 return %leds;
593 }
594
595 foreach my $gpio (@{$connections->{CONN}}) {
596 my %ledNode;
597
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500598 $ledNode{COMMENT} = connectionComment($gpio);
Matt Spinler995f2a22016-09-30 13:07:31 -0500599
600 #The node name will be the simplified LED name
601 my $name = $gpio->{DEST_PARENT};
602 $name =~ s/(-\d+$)//; #remove trailing position
603 $name =~ s/.*\///; #remove the front of the path
604
605 #For now only supports ASPEED.
606 if (uc($g_bmcMfgr) ne "ASPEED") {
607 die "ERROR: Unsupported BMC manufacturer $g_bmcMfgr\n";
608 }
609 my $num = $g_targetObj->getAttribute($gpio->{SOURCE}, "PIN_NUM");
610 my $macro = getAspeedGpioMacro($num);
611
612 #If it's active high or low
613 my $state = $g_targetObj->getAttribute($gpio->{DEST_PARENT}, "ON_STATE");
614 my $activeString = getGpioActiveString($state);
615
616 $ledNode{gpios} = "<&gpio $macro $activeString>";
617
618 $leds{$name} = { %ledNode };
619 }
620
621 return %leds;
622}
623
624
625#Returns a either GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW
626# $val = either a 1 or a 0 for active high or low
Matt Spinler889343f2017-01-30 14:14:11 -0600627sub getGpioActiveString
628{
Matt Spinler995f2a22016-09-30 13:07:31 -0500629 my $val = shift;
630
631 if ($val == 0) {
632 return "GPIO_ACTIVE_LOW";
633 }
634
635 return "GPIO_ACTIVE_HIGH";
636}
637
638
639#Turns a GPIO number into something like ASPEED_GPIO(A, 0) for the
640#ASPEED GPIO numbering scheme A[0-7] -> Z[0-7] and then starts at
641#AA[0-7] after that.
642# $num = the GPIO number
Matt Spinler889343f2017-01-30 14:14:11 -0600643sub getAspeedGpioMacro
644{
Matt Spinler995f2a22016-09-30 13:07:31 -0500645 my $num = shift;
646 my $char;
647 my $offset = $num % 8;
648 my $block = int($num / 8);
649
650 #If past Z, wraps to AA, AB, etc
651 if ((ord('A') + $block) > ord('Z')) {
652 #how far past Z?
653 $char = $block - (ord('Z') - ord('A'));
654
655 #Don't let it wrap twice
656 if ($char > (ord('Z') - ord('A') + 1)) {
657 die "ERROR: Invalid PIN_NUM value $num found for GPIO\n";
658 }
659
660 #start back at 'A' again, and convert to a character
661 $char = chr($char + ord('A') - 1);
662
663 #Add in a bonus 'A', to get something like AB
664 $char = "A".$char;
665 }
666 else {
667 $char = ord('A') + $block;
668 $char = chr($char);
669 }
670
671 return "ASPEED_GPIO($char, $offset)";
672}
673
674
675#Returns a list of hashes that represent the UART nodes on the BMC by
676#finding the UART connections.
677#Nodes will look like:
678# &uartX {
679# status = "okay"
680# }
Matt Spinler889343f2017-01-30 14:14:11 -0600681sub getUARTNodes
Matt Spinler995f2a22016-09-30 13:07:31 -0500682{
683 my @nodes;
684
Matt Spinler23d47c22016-10-04 12:31:21 -0500685 #Using U750 for legacy MRW reasons
Matt Spinler18d5f572016-11-15 15:25:45 -0600686 my $connections = $g_targetObj->findConnections($g_bmc, "U750");
Matt Spinler995f2a22016-09-30 13:07:31 -0500687
688 if ($connections eq "") {
689 print "WARNING: No UART buses found connected to the BMC\n";
690 return @nodes;
691 }
692
693 foreach my $uart (@{$connections->{CONN}}) {
694 my %node;
695
696 my $num = $g_targetObj->getAttribute($uart->{SOURCE}, "CHIP_UNIT");
697 my $name = "uart$num";
698
699 $node{$name}{status} = "okay";
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500700 $node{$name}{COMMENT} = connectionComment($uart);
Matt Spinler995f2a22016-09-30 13:07:31 -0500701
Matt Spinler2efdcba2016-11-08 15:37:20 -0600702 #Add in any pinctrl properties. These would come from the parent
703 #of $uart{SOURCE}, which would be a unit-pingroup-bmc if the
704 #pins for this connection are multi-function.
705 addPinCtrlProps($g_targetObj->getTargetParent($uart->{SOURCE}),
706 \%{$node{$name}});
707
Matt Spinler995f2a22016-09-30 13:07:31 -0500708 push @nodes, { %node };
709 }
710
Matt Spinler7d381e12016-09-27 14:27:24 -0500711 return @nodes;
712}
713
714
Matt Spinler995f2a22016-09-30 13:07:31 -0500715#Returns a list of hashes that represent the MAC (ethernet) nodes on the BMC
716#by finding the connections of type ETHERNET.
717#Nodes will look like:
718# &macX {
719# ...
720# }
Matt Spinler889343f2017-01-30 14:14:11 -0600721sub getMacNodes
Matt Spinler995f2a22016-09-30 13:07:31 -0500722{
723 my @nodes;
724
Matt Spinler18d5f572016-11-15 15:25:45 -0600725 my $connections = $g_targetObj->findConnections($g_bmc, "ETHERNET");
Matt Spinler995f2a22016-09-30 13:07:31 -0500726
727 if ($connections eq "") {
728 print "WARNING: No ethernet buses found connected to the BMC\n";
729 return @nodes;
730 }
731
732 foreach my $eth (@{$connections->{CONN}}) {
733 my %node;
734
735 my $num = $g_targetObj->getAttribute($eth->{SOURCE}, "CHIP_UNIT");
736 my $ncsi = $g_targetObj->getAttribute($eth->{SOURCE}, "NCSI_MODE");
737 my $hwChecksum = $g_targetObj->getAttribute($eth->{SOURCE},
738 "USE_HW_CHECKSUM");
739
740 my $name = "mac$num";
741 $node{$name}{status} = "okay";
742
743 if ($ncsi == 1) {
Matt Spinler74909132016-10-07 13:52:19 -0500744 $node{$name}{"use-ncsi"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500745 }
746 if ($hwChecksum == 0) {
Matt Spinler74909132016-10-07 13:52:19 -0500747 $node{$name}{"no-hw-checksum"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500748 }
749
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500750 $node{$name}{COMMENT} = connectionComment($eth);
Matt Spinler995f2a22016-09-30 13:07:31 -0500751
Matt Spinler2efdcba2016-11-08 15:37:20 -0600752 #Add in any pinctrl properties. These would come from the parent
753 #of $eth{SOURCE}, which would be a unit-pingroup-bmc if the
754 #pins for this connection are multi-function.
755 addPinCtrlProps($g_targetObj->getTargetParent($eth->{SOURCE}),
756 \%{$node{$name}});
757
Matt Spinler995f2a22016-09-30 13:07:31 -0500758 push @nodes, { %node };
759 }
760
761 return @nodes;
762}
763
764
765#Returns a list of hashes that represent the virtual UART nodes
766#Node will look like:
767# &vuart {
768# status = "okay"
769# }
Matt Spinler889343f2017-01-30 14:14:11 -0600770sub getVuartNodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500771{
772 my @nodes;
773 my %node;
774
775 #For now, enable 1 node all the time.
Matt Spinler995f2a22016-09-30 13:07:31 -0500776 #TBD if this needs to be fixed
Matt Spinler7d381e12016-09-27 14:27:24 -0500777 $node{vuart}{status} = "okay";
778
779 push @nodes, { %node };
780
781 return @nodes;
782}
783
Matt Spinler74909132016-10-07 13:52:19 -0500784#Returns a list of hashes that represent the I2C device nodes.
785#There is 1 parent node for each bus, which then have subnodes
786#for each device on that bus. If a bus doesn't have any
787#attached devices, it doesn't need to show up.
788#The nodes will look like:
789# &i2c0 {
790# status = "okay"
791# device1@addr { (addr = 7 bit I2C address)
792# reg = <addr>
793# compatible = ...
794# ...
795# }
796# device2@addr {
797# reg = <addr>
798# ...
799# }
800# }
801# &i2c1 {
802# ...
803# }
Matt Spinler889343f2017-01-30 14:14:11 -0600804sub getI2CNodes
Matt Spinler74909132016-10-07 13:52:19 -0500805{
806 my @nodes;
807 my %busNodes;
808
Matt Spinler18d5f572016-11-15 15:25:45 -0600809 my $connections = $g_targetObj->findConnections($g_bmc, "I2C");
Matt Spinler74909132016-10-07 13:52:19 -0500810
811 if ($connections eq "") {
812 print "WARNING: No I2C buses found connected to the BMC\n";
813 return @nodes;
814 }
815
816 foreach my $i2c (@{$connections->{CONN}}) {
817
818 my %deviceNode, my $deviceName;
819
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500820 $deviceNode{COMMENT} = connectionComment($i2c);
Matt Spinler74909132016-10-07 13:52:19 -0500821
822 $deviceName = lc $i2c->{DEST_PARENT};
823 $deviceName =~ s/-\d+$//; #remove trailing position
824 $deviceName =~ s/.*\///; #remove the front of the path
825
826 #Get the I2C address
827 my $i2cAddress = $g_targetObj->getAttribute($i2c->{DEST}, "I2C_ADDRESS");
828 $i2cAddress = hex($i2cAddress);
829 if ($i2cAddress == 0) {
830 die "ERROR: Missing I2C address on $i2c->{DEST}\n";
831 }
832
833 #Put it in the format we want to print it in
834 $i2cAddress = adjustI2CAddress($i2cAddress);
835 $deviceNode{reg} = "<$i2cAddress>";
836
837 $deviceName = makeNodeName($deviceName, $deviceNode{reg});
838
839 #Get the I2C bus number
840 if ($g_targetObj->isBadAttribute($i2c->{SOURCE},
841 "I2C_PORT")) {
842 die "ERROR: I2C_PORT attribute in $i2c->{DEST_PARENT} " .
843 "is either missing or empty.\n";
844 }
845
846 my $busNum = $g_targetObj->getAttribute($i2c->{SOURCE}, "I2C_PORT");
847 if ($busNum =~ /0x/i) {
848 $busNum = hex($busNum);
849 }
850
851 #Convert the number to the Linux numbering scheme.
Matt Spinler30b461c2016-10-10 16:50:07 -0500852 $busNum += $g_i2cBusAdjust;
Matt Spinler74909132016-10-07 13:52:19 -0500853
854 #Get the compatible property
855 if ($g_targetObj->isBadAttribute($i2c->{DEST_PARENT},
856 "BMC_DT_COMPATIBLE")) {
857 die "ERROR: BMC_DT_COMPATIBLE attribute in $i2c->{DEST_PARENT} " .
858 "is either missing or empty.\n";
859 }
860
861 $deviceNode{compatible} = $g_targetObj->getAttribute(
862 $i2c->{DEST_PARENT},
863 "BMC_DT_COMPATIBLE");
864
865 #Get any other part specific properties, where the property
866 #names are actually defined in the XML.
867 my %props = getPartDefinedDTProperties($i2c->{DEST_PARENT});
868 foreach my $prop (sort keys %props) {
869 $deviceNode{$prop} = $props{$prop};
870 }
871
872 #busNodeName is the hash twice so when we loop
873 #below it doesn't get lost
874 my $busNodeName = "i2c$busNum";
875 $busNodes{$busNodeName}{$busNodeName}{status} = "okay";
876 $busNodes{$busNodeName}{$busNodeName}{$deviceName} = { %deviceNode };
Matt Spinler2efdcba2016-11-08 15:37:20 -0600877
878 #Add in any pinctrl properties. These would come from the parent
879 #of $i2c{SOURCE}, which would be a unit-pingroup-bmc if the
880 #pins for this connection are multi-function.
881 addPinCtrlProps($g_targetObj->getTargetParent($i2c->{SOURCE}),
882 \%{$busNodes{$busNodeName}{$busNodeName}});
Matt Spinler74909132016-10-07 13:52:19 -0500883 }
884
885 #Each bus gets its own hash entry in the array
886 for my $b (sort keys %busNodes) {
887 push @nodes, { %{$busNodes{$b}} };
888 }
889
890 return @nodes;
891}
892
893
894#Returns a hash of property names and values that should be stored in
895#the device tree node for this device. The names of the properties and
896#the attributes to find their values in are stored in the
897#BMC_DT_ATTR_NAMES attribute in the chip.
898# $chip = the chip target
Matt Spinler889343f2017-01-30 14:14:11 -0600899sub getPartDefinedDTProperties
Matt Spinler74909132016-10-07 13:52:19 -0500900{
901 my $chip = shift;
902 my %props;
903
904 if ($g_targetObj->isBadAttribute($chip, "BMC_DT_ATTR_NAMES")) {
905 return %props;
906 }
907
908 my $attr = $g_targetObj->getAttribute($chip, "BMC_DT_ATTR_NAMES");
909 $attr =~ s/\s//g;
910 my @names = split(',', $attr);
911
912 #There can be up to 4 entries in this attribute
913 for (my $i = 0; $i < scalar @names; $i += 2) {
914
915 #$names[$i] holds the name of the attribute.
916 #$names[$i+1] holds the name of the property to store its value in.
917 if (($names[$i] ne "NA") && ($names[$i] ne "")) {
918
919 my $val = $g_targetObj->getAttribute($chip, $names[$i]);
920
921 #if the value is empty, assume it's for a standalone property,
922 #which gets turned into: some-property;
923 if ($val eq "") {
924 $props{$names[$i+1]} = ZERO_LENGTH_PROPERTY;
925 }
926 else {
927 $props{$names[$i+1]} = "<$val>";
928 }
929 }
930 }
931
932 return %props;
933}
934
935
936#Convert the MRW I2C address into the format the dts needs
937# $addr = the I2C Address
Matt Spinler889343f2017-01-30 14:14:11 -0600938sub adjustI2CAddress
Matt Spinler74909132016-10-07 13:52:19 -0500939{
940 my $addr = shift;
941
942 #MRW holds the 8 bit value. We need the 7 bit one.
Matt Spinler96f8f242016-11-28 16:26:57 -0600943 $addr = $addr >> 1;
Matt Spinler74909132016-10-07 13:52:19 -0500944 $addr = sprintf("0x%X", $addr);
945 $addr = lc $addr;
946
947 return $addr;
948}
949
950
Matt Spinler30b461c2016-10-10 16:50:07 -0500951#Sets the global $g_i2cBusAdjust from the configuration file.
Matt Spinler889343f2017-01-30 14:14:11 -0600952sub getI2CBusAdjust
Matt Spinler74909132016-10-07 13:52:19 -0500953{
Matt Spinler30b461c2016-10-10 16:50:07 -0500954 if (exists $g_configuration{"i2c-bus-adjust"}) {
Matt Spinler74909132016-10-07 13:52:19 -0500955
Matt Spinler30b461c2016-10-10 16:50:07 -0500956 $g_i2cBusAdjust = $g_configuration{"i2c-bus-adjust"};
Matt Spinler74909132016-10-07 13:52:19 -0500957
Matt Spinler30b461c2016-10-10 16:50:07 -0500958 if (!looks_like_number($g_i2cBusAdjust)) {
959 die "ERROR: Invalid i2c-bus-adjust value $g_i2cBusAdjust " .
960 "found in config file.\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500961 }
962 }
Matt Spinler30b461c2016-10-10 16:50:07 -0500963 else {
964 $g_i2cBusAdjust = 0;
965 print "WARNING: No I2C Bus number adjustment done " .
966 "for this system.\n";
967 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500968}
969
970
Matt Spinler2efdcba2016-11-08 15:37:20 -0600971
972#Adds two pinctrl properties to the device node hash passed in,
973#if specified in the MRW. Pin Control refers to a mechanism for
974#Linux to know which function of a multi-function pin to configure.
975#For example, a pin could either be configured to be a GPIO, or
976#an I2C clock line. The pin function depends on board wiring,
977#so is known by the MRW.
978# $target = the target to get the BMC_DT_PINCTRL_FUNCTS attribute from
979# $node = a hash reference to the device tree node to add the properties to
Matt Spinler889343f2017-01-30 14:14:11 -0600980sub addPinCtrlProps
Matt Spinler2efdcba2016-11-08 15:37:20 -0600981{
982 my ($target, $node) = @_;
983
984 if (!$g_targetObj->isBadAttribute($target, "BMC_DT_PINCTRL_FUNCS")) {
985 my $attr = $g_targetObj->getAttribute($target,
986 "BMC_DT_PINCTRL_FUNCS");
987
988 my $pinCtrl0Prop = makePinCtrl0PropValue($attr);
989 if ($pinCtrl0Prop ne "") {
990 $node->{"pinctrl-names"} = "default";
991 $node->{"pinctrl-0"} = $pinCtrl0Prop;
992 }
993 }
994}
995
996
997#Constructs the pinctrl-0 property value based on the
998#BMC_DT_PINCTRL_FUNCS attribute passed in.
999# $attr = BMC_DT_PINCTRL_FUNCS attribute value, which is an array
Matt Spinler889343f2017-01-30 14:14:11 -06001000sub makePinCtrl0PropValue
Matt Spinler2efdcba2016-11-08 15:37:20 -06001001{
1002 my $attr = shift;
1003 my @entries;
1004 my $value = "";
1005
1006 $attr =~ s/\s//g;
1007 my @funcs = split(',', $attr);
1008 foreach my $func (@funcs) {
1009 if (($func ne "NA") && ($func ne "")) {
1010 push @entries, $func;
1011 }
1012 }
1013
1014 #<&pinctrl_funcA_default &pinctrl_funcB_default ...>
1015 if (scalar @entries) {
1016 $value = "<";
1017 foreach my $entry (@entries) {
1018 $value .= "&pinctrl_".$entry."_default ";
1019 }
1020 $value =~ s/\s$//; #Remove the trailing space
1021 $value .= ">";
1022 }
1023
1024 return $value;
1025}
1026
1027
Matt Spinler7d381e12016-09-27 14:27:24 -05001028#Returns a list of compatible fields for the BMC itself.
Matt Spinler889343f2017-01-30 14:14:11 -06001029sub getBMCCompatibles
Matt Spinler7d381e12016-09-27 14:27:24 -05001030{
1031 my @compats;
1032
Matt Spinler23d47c22016-10-04 12:31:21 -05001033 #1st entry: <system mfgr>,<system name>-bmc
1034 #2nd entry: <bmc mfgr>,<bmc model>
Matt Spinler7d381e12016-09-27 14:27:24 -05001035
Matt Spinler23d47c22016-10-04 12:31:21 -05001036 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() }) {
1037 if ($g_targetObj->getType($target) eq "SYS") {
1038 my $mfgr = $g_targetObj->getAttribute($target, "MANUFACTURER");
1039 push @compats, lc "$mfgr,$g_systemName-bmc";
1040 last;
1041 }
Matt Spinler7d381e12016-09-27 14:27:24 -05001042 }
1043
1044 push @compats, lc($g_bmcMfgr).",".lc($g_bmcModel);
1045
1046 return @compats;
1047}
1048
1049
1050#Returns a string for the system's BMC model property
Matt Spinler889343f2017-01-30 14:14:11 -06001051sub getSystemBMCModel
Matt Spinler7d381e12016-09-27 14:27:24 -05001052{
Matt Spinler995f2a22016-09-30 13:07:31 -05001053 #'<System> BMC'
Matt Spinler7d381e12016-09-27 14:27:24 -05001054 my $sys = lc $g_systemName;
1055 $sys = uc(substr($sys, 0, 1)) . substr($sys, 1);
1056
1057 return $sys . " BMC";
1058}
1059
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001060#Create the comment that will show up in the device tree
1061#for a connection. In the output, will look like:
1062# // sourceUnit ->
1063# // destChip
1064#
1065# $conn = The connection hash reference
Matt Spinler889343f2017-01-30 14:14:11 -06001066sub connectionComment
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001067{
1068 my $conn = shift;
1069 my $comment = "$conn->{SOURCE} ->\n$conn->{DEST_PARENT}";
1070 return $comment;
1071}
1072
Matt Spinler7d381e12016-09-27 14:27:24 -05001073
1074#Prints a list of nodes at the same indent level
1075# $f = file handle
1076# $level = indent level (0,1,etc)
1077# @nodes = array of node hashes to print, where the
1078# key for the hash is the name of the node
Matt Spinler889343f2017-01-30 14:14:11 -06001079sub printNodes
Matt Spinler7d381e12016-09-27 14:27:24 -05001080{
1081 my ($f, $level, @nodes) = @_;
1082
1083 foreach my $n (@nodes) {
1084 my %node = %$n;
1085
1086 foreach my $name (sort keys %node) {
1087 my %n = %{ $node{$name} };
1088 printNode($f, $level, $name, %n);
1089 }
1090 }
1091}
1092
1093
1094#Print a single node and its children
1095# $f = file handle
1096# $level = indent level (0,1,etc)
1097# $name = the name of the node - shows up as:
1098# name { ...
1099# %vals = The contents of the node, with the following options:
1100# if the key is:
1101# - 'DTSI_INCLUDE', then value gets turned into a #include
Matt Spinler995f2a22016-09-30 13:07:31 -05001102# - 'COMMENT', then value gets turned into a // comment
Matt Spinler74909132016-10-07 13:52:19 -05001103# - 'ZERO_LENGTH_PROPERTY' then value gets turned into: value;
Matt Spinler7d381e12016-09-27 14:27:24 -05001104#
1105# If the value is:
1106# - a hash - then that hash gets turned into a child node
1107# where the key is the name of the child node
Matt Spinler995f2a22016-09-30 13:07:31 -05001108# - an array of hashes indicates an array of child nodes
Matt Spinler889343f2017-01-30 14:14:11 -06001109sub printNode
Matt Spinler7d381e12016-09-27 14:27:24 -05001110{
1111 my ($f, $level, $name, %vals) = @_;
1112 my $include = "";
1113
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001114 #No reason to print an empty node
1115 if (!keys %vals) {
1116 return;
1117 }
1118
Matt Spinler7d381e12016-09-27 14:27:24 -05001119 if ($level == 0) {
1120 $name = "&".$name;
1121 }
1122
Matt Spinler995f2a22016-09-30 13:07:31 -05001123 print $f "\n";
1124
1125 if (exists $vals{COMMENT}) {
1126 my @lines = split('\n', $vals{COMMENT});
1127 foreach my $l (@lines) {
1128 print $f indent($level) . "// $l\n";
1129 }
1130 }
1131
Matt Spinler6d391252017-01-31 13:46:06 -06001132 #The node can have a label, which looks like:
1133 #label : name {
1134 my $label = "";
1135 if (exists $vals{NODE_LABEL}) {
1136 $label = $vals{NODE_LABEL} . ": ";
1137 }
1138
1139 print $f indent($level) . $label . "$name {\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001140
Matt Spinler74909132016-10-07 13:52:19 -05001141 #First print properties, then includes, then subnodes
1142
1143 #Print Properties
Matt Spinler7d381e12016-09-27 14:27:24 -05001144 foreach my $v (sort keys %vals) {
1145
Matt Spinler995f2a22016-09-30 13:07:31 -05001146 next if ($v eq "COMMENT");
Matt Spinler74909132016-10-07 13:52:19 -05001147 next if ($v eq "DTSI_INCLUDE");
Matt Spinler6d391252017-01-31 13:46:06 -06001148 next if ($v eq "NODE_LABEL");
Matt Spinler74909132016-10-07 13:52:19 -05001149 next if (ref($vals{$v}) eq "HASH");
1150 next if (ref($vals{$v}) eq "ARRAY");
Matt Spinler995f2a22016-09-30 13:07:31 -05001151
Matt Spinler74909132016-10-07 13:52:19 -05001152 if ($vals{$v} ne ZERO_LENGTH_PROPERTY) {
1153 printProperty($f, $level+1, $v, $vals{$v});
Matt Spinler7d381e12016-09-27 14:27:24 -05001154 }
Matt Spinler74909132016-10-07 13:52:19 -05001155 else {
1156 printZeroLengthProperty($f, $level+1, $v);
1157 }
1158 }
1159
1160 #Print Includes
1161 foreach my $v (sort keys %vals) {
1162
1163 if ($v eq "DTSI_INCLUDE") {
1164 #print 1 include per line
1165 my @incs = split(',', $vals{$v});
1166 foreach my $i (@incs) {
1167 print $f qq(#include "$i";\n);
1168 }
1169 }
1170 }
1171
1172 #Print Nodes
1173 foreach my $v (sort keys %vals) {
1174
1175 if (ref($vals{$v}) eq "HASH") {
Matt Spinler7d381e12016-09-27 14:27:24 -05001176 printNode($f, $level+1, $v, %{$vals{$v}});
1177 }
Matt Spinler995f2a22016-09-30 13:07:31 -05001178 #An array of nested nodes
1179 elsif (ref($vals{$v}) eq "ARRAY") {
1180 my @array = @{$vals{$v}};
1181 &printNodes($f, $level+1, @array);
1182 }
Matt Spinler7d381e12016-09-27 14:27:24 -05001183 }
1184
1185 print $f indent($level) . "};\n";
1186}
1187
1188
1189#Prints a comma separated list of properties.
1190#e.g. a = "b, c, d";
1191# $f = file handle
1192# $level = indent level (0,1,etc)
1193# $name = name of property
1194# @vals = list of property values
Matt Spinler889343f2017-01-30 14:14:11 -06001195sub printPropertyList
Matt Spinler7d381e12016-09-27 14:27:24 -05001196{
1197 my ($f, $level, $name, @vals) = @_;
1198
1199 print $f indent($level) . "$name = ";
1200
1201 for (my $i = 0;$i < scalar @vals; $i++) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001202 print $f qq("$vals[$i]");
Matt Spinler7d381e12016-09-27 14:27:24 -05001203 if ($i < (scalar(@vals) - 1)) {
1204 print $f ", ";
1205 }
1206 }
1207 print $f ";\n"
1208}
1209
1210
1211#Prints a single property. e.g. a = "b";
1212# $f = file handle
1213# $level = indent level (0,1,etc)
1214# $name = name of property
1215# @vals = property values
Matt Spinler889343f2017-01-30 14:14:11 -06001216sub printProperty
Matt Spinler7d381e12016-09-27 14:27:24 -05001217{
1218 my ($f, $level, $name, $val) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001219 my $quoteChar = qq(");
Matt Spinler23d47c22016-10-04 12:31:21 -05001220
Matt Spinler30b461c2016-10-10 16:50:07 -05001221 $val = convertReference($val);
Matt Spinler23d47c22016-10-04 12:31:21 -05001222
1223 #properties with < > or single word aliases don't need quotes
1224 if (($val =~ /<.*>/) || ($val =~ /^&\w+$/)) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001225 $quoteChar = "";
Matt Spinler23d47c22016-10-04 12:31:21 -05001226 }
1227
Matt Spinler30b461c2016-10-10 16:50:07 -05001228 print $f indent($level) . "$name = $quoteChar$val$quoteChar;\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001229}
1230
1231
Matt Spinler30b461c2016-10-10 16:50:07 -05001232#Prints a zero length property e.g. some-property;
Matt Spinler7d381e12016-09-27 14:27:24 -05001233# $f = file handle
1234# $level = indent level (0,1,etc)
1235# $name = name of property
Matt Spinler889343f2017-01-30 14:14:11 -06001236sub printZeroLengthProperty
Matt Spinler7d381e12016-09-27 14:27:24 -05001237{
1238 my ($f, $level, $name) = @_;
1239 print $f indent($level) . "$name;\n";
1240}
1241
1242
Matt Spinler30b461c2016-10-10 16:50:07 -05001243#Replace '(ref)' with '&'.
Matt Spinler7d381e12016-09-27 14:27:24 -05001244#Needed because Serverwiz doesn't properly escape '&'s in the XML,
Matt Spinler30b461c2016-10-10 16:50:07 -05001245#so the '(ref)' string is used to represent the reference
Matt Spinler7d381e12016-09-27 14:27:24 -05001246#specifier instead of '&'.
Matt Spinler889343f2017-01-30 14:14:11 -06001247sub convertReference
1248{
Matt Spinler7d381e12016-09-27 14:27:24 -05001249 my $val = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -05001250 $val =~ s/\(ref\)/&/g;
Matt Spinler7d381e12016-09-27 14:27:24 -05001251 return $val
1252}
1253
1254
Matt Spinler7d381e12016-09-27 14:27:24 -05001255#Prints the device tree version line.
1256# $f = file handle
Matt Spinler889343f2017-01-30 14:14:11 -06001257sub printVersion
Matt Spinler7d381e12016-09-27 14:27:24 -05001258{
1259 my $f = shift;
1260 print $f VERSION."\n"
1261}
1262
1263
1264#Prints the #include line for pulling in an include file.
Matt Spinler30b461c2016-10-10 16:50:07 -05001265#The files to include come from the configuration file.
Matt Spinler7d381e12016-09-27 14:27:24 -05001266# $f = file handle
Matt Spinler30b461c2016-10-10 16:50:07 -05001267# $type = include type
Matt Spinler889343f2017-01-30 14:14:11 -06001268sub printIncludes
Matt Spinler7d381e12016-09-27 14:27:24 -05001269{
Matt Spinler30b461c2016-10-10 16:50:07 -05001270 my ($f, $type) = @_;
1271 my @includes = getIncludes($type);
Matt Spinler7d381e12016-09-27 14:27:24 -05001272
1273 foreach my $i (@includes) {
1274 #if a .dtsi, gets " ", otherwise < >
1275 if ($i =~ /\.dtsi$/) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001276 $i = qq("$i");
Matt Spinler7d381e12016-09-27 14:27:24 -05001277 }
1278 else {
Matt Spinler30b461c2016-10-10 16:50:07 -05001279 $i = "<$i>";
Matt Spinler7d381e12016-09-27 14:27:24 -05001280 }
Matt Spinler30b461c2016-10-10 16:50:07 -05001281 print $f "#include $i\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001282 }
1283}
1284
1285
Matt Spinler30b461c2016-10-10 16:50:07 -05001286#Returns an array of include files found in the config file
1287#for the type specified.
1288# $type = the include type, which is the section name in the
1289# YAML configuration file.
Matt Spinler889343f2017-01-30 14:14:11 -06001290sub getIncludes
Matt Spinler7d381e12016-09-27 14:27:24 -05001291{
Matt Spinler30b461c2016-10-10 16:50:07 -05001292 my $type = shift;
Matt Spinler7d381e12016-09-27 14:27:24 -05001293 my @includes;
1294
Matt Spinler30b461c2016-10-10 16:50:07 -05001295 #The config file may have a section but no includes
1296 #listed in it, which is OK.
1297 if ((exists $g_configuration{includes}{$type}) &&
1298 (ref($g_configuration{includes}{$type}) eq "ARRAY")) {
Matt Spinler7d381e12016-09-27 14:27:24 -05001299
Matt Spinler30b461c2016-10-10 16:50:07 -05001300 @includes = @{$g_configuration{includes}{$type}};
Matt Spinler7d381e12016-09-27 14:27:24 -05001301 }
1302
1303 return @includes;
1304}
1305
Matt Spinler30b461c2016-10-10 16:50:07 -05001306
Matt Spinler74909132016-10-07 13:52:19 -05001307#Appends the first value of the 'reg' property
1308#passed in to the name passed in to create the
1309#full name for the node
1310# $name = node name that will be appended to
1311# $reg = the reg property values
Matt Spinler889343f2017-01-30 14:14:11 -06001312sub makeNodeName
Matt Spinler74909132016-10-07 13:52:19 -05001313{
1314 my ($name, $reg) = @_;
1315
1316 $reg =~ s/<//g;
1317 $reg =~ s/>//g;
1318 my @vals = split(' ', $reg);
1319
1320 if (scalar @vals > 0) {
1321 $vals[0] =~ s/0x//;
1322 $name .= "@" . lc $vals[0];
1323 }
1324
1325 return $name;
1326}
1327
Matt Spinler7d381e12016-09-27 14:27:24 -05001328
1329#Prints the root node starting bracket.
1330# $f = file handle
Matt Spinler889343f2017-01-30 14:14:11 -06001331sub printRootNodeStart
1332{
Matt Spinler7d381e12016-09-27 14:27:24 -05001333 my $f = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -05001334 print $f qq(/ {\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001335}
1336
1337
1338#Prints the root node ending bracket.
1339# $f = file handle
1340# $level = indent level (0,1,etc)
Matt Spinler889343f2017-01-30 14:14:11 -06001341sub printRootNodeEnd
1342{
Matt Spinler7d381e12016-09-27 14:27:24 -05001343 my ($f, $level) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001344 print $f indent($level).qq(};\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001345}
1346
1347
1348#Returns a string that can be used to indent based on the
1349#level passed in. Each level is an additional 4 spaces.
1350# $level = indent level (0,1,etc)
Matt Spinler889343f2017-01-30 14:14:11 -06001351sub indent
1352{
Matt Spinler7d381e12016-09-27 14:27:24 -05001353 my $level = shift;
1354 return ' ' x ($level * 4);
1355}
1356
1357
Matt Spinler7d381e12016-09-27 14:27:24 -05001358sub printUsage
1359{
Matt Spinler30b461c2016-10-10 16:50:07 -05001360 print "gen_devtree.pl -x [XML filename] -y [yaml config file] " .
1361 "-o [output filename]\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001362 exit(1);
1363}