blob: fabae8ec2d9c1bbad9e9be421867372c20f1129f [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 Spinler995f2a22016-09-30 13:07:31 -050079printNodes($f, 0, getUARTNodes());
Matt Spinler41dcb622017-01-31 14:55:19 -060080printNodes($f, 0, getMacNodes());
81
82printNodes($f, 0, getI2CNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050083printNodes($f, 0, getVuartNodes());
84
Matt Spinler30b461c2016-10-10 16:50:07 -050085printIncludes($f, POST_ROOT_INCLUDES);
86
Matt Spinler7d381e12016-09-27 14:27:24 -050087close $f;
88exit 0;
89
90
Matt Spinler74909132016-10-07 13:52:19 -050091#Finds the values for these globals:
92# $g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName
Matt Spinler889343f2017-01-30 14:14:11 -060093sub setGlobalAttributes
Matt Spinler74909132016-10-07 13:52:19 -050094{
Matt Spinlere2bf4392017-01-30 13:16:28 -060095 $g_bmc = Util::getBMCTarget($g_targetObj);
Matt Spinler74909132016-10-07 13:52:19 -050096
97 if ($g_targetObj->isBadAttribute($g_bmc, "MODEL")) {
98 die "The MODEL attribute on $g_bmc is missing or empty.\n";
99 }
100 $g_bmcModel = $g_targetObj->getAttribute($g_bmc, "MODEL");
101
102 if ($g_targetObj->isBadAttribute($g_bmc, "MANUFACTURER")) {
103 die "The MANUFACTURER attribute on $g_bmc is missing or empty.\n";
104 }
105 $g_bmcMfgr = $g_targetObj->getAttribute($g_bmc, "MANUFACTURER");
106
107 $g_systemName = $g_targetObj->getSystemName();
108 if (length($g_systemName) == 0) {
109 die "The SYSTEM_NAME attribute is not set on the system target.\n";
110 }
111}
112
113
Matt Spinler23d47c22016-10-04 12:31:21 -0500114#Returns a hash that represents the 'aliases' node.
115#Will look like:
116# aliases {
117# name1 = &val1;
118# name2 = &val2;
119# ...
120# }
Matt Spinler889343f2017-01-30 14:14:11 -0600121sub getAliases
Matt Spinler23d47c22016-10-04 12:31:21 -0500122{
123 my %aliases;
Matt Spinler23d47c22016-10-04 12:31:21 -0500124
Matt Spinler30b461c2016-10-10 16:50:07 -0500125 #Get the info from the config file
Matt Spinler23d47c22016-10-04 12:31:21 -0500126
Matt Spinler30b461c2016-10-10 16:50:07 -0500127 if ((not exists $g_configuration{aliases}) ||
128 (keys %{$g_configuration{aliases}} == 0)) {
129 print "WARNING: Missing or empty 'aliases' section in config file.\n";
130 return %aliases;
131 }
132 %aliases = %{ $g_configuration{aliases} };
133
134 #add a & reference if one is missing
135 foreach my $a (keys %aliases) {
136 if (($aliases{$a} !~ /^&/) && ($aliases{$a} !~ /^\(ref\)/)) {
137 $aliases{$a} = "(ref)$aliases{$a}";
Matt Spinler23d47c22016-10-04 12:31:21 -0500138 }
139 }
140
141 return %aliases;
142}
143
Matt Spinler7d381e12016-09-27 14:27:24 -0500144
145#Return a hash that represents the 'chosen' node
Matt Spinler995f2a22016-09-30 13:07:31 -0500146#Will look like:
147# chosen {
148# stdout-path = ...
149# bootargs = ...
150# }
Matt Spinler889343f2017-01-30 14:14:11 -0600151sub getChosen
Matt Spinler7d381e12016-09-27 14:27:24 -0500152{
Matt Spinler7d381e12016-09-27 14:27:24 -0500153 my %chosen;
Matt Spinler30b461c2016-10-10 16:50:07 -0500154 my @allowed = qw(bootargs stdin-path stdout-path);
155
156 #Get the info from the config file
157
158 if (not exists $g_configuration{chosen}) {
159 die "ERROR: Missing 'chosen' section in config file.\n";
160 }
161 %chosen = %{ $g_configuration{chosen} };
162
Matt Spinler30b461c2016-10-10 16:50:07 -0500163 foreach my $key (keys %chosen) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500164
Matt Spinler28fb1a92017-01-30 12:54:10 -0600165 #Check for allowed entries. Empty is OK.
166 if (!grep(/^$key$/, @allowed)) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500167 die "Invalid entry $key in 'chosen' section in config file\n";
168 }
Matt Spinler28fb1a92017-01-30 12:54:10 -0600169
170 #stdout-path and stdin-path can use aliases, which will look like
171 #(alias)uart5 in the yaml. Change to (ref)uart5 so it will be
172 #converted to a '&' later.
173 $chosen{$key} =~ s/\(alias\)/\(ref\)/g;
Matt Spinler30b461c2016-10-10 16:50:07 -0500174 }
175
Matt Spinler7d381e12016-09-27 14:27:24 -0500176 return %chosen;
177}
178
179
Matt Spinler30b461c2016-10-10 16:50:07 -0500180#Return a hash that represents the 'memory' node.
Matt Spinler995f2a22016-09-30 13:07:31 -0500181#Will look like:
Matt Spinler30b461c2016-10-10 16:50:07 -0500182# memory {
183# reg = < base size >
184# }
Matt Spinler889343f2017-01-30 14:14:11 -0600185sub getBmcMemory
Matt Spinler30b461c2016-10-10 16:50:07 -0500186{
187 my %memory;
188
189 #Get the info from the config file
190
191 if (not exists $g_configuration{memory}) {
192 die "ERROR: Missing 'memory' section in config file.\n";
193 }
194
195 if ((not exists $g_configuration{memory}{base}) ||
196 ($g_configuration{memory}{base} !~ /0x/)) {
197 die "ERROR: The base entry in the memory section in the config " .
198 "file is either missing or invalid.\n";
199 }
200
201 if ((not exists $g_configuration{memory}{size}) ||
202 ($g_configuration{memory}{size} !~ /0x/)) {
203 die "ERROR: The size entry in the memory section in the config " .
204 "file is either missing or invalid.\n";
205 }
206
207 #Future: could do more validation on the actual values
208
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600209 addRegProp(\%memory,
210 $g_configuration{memory}{base},
211 $g_configuration{memory}{size});
Matt Spinler30b461c2016-10-10 16:50:07 -0500212
213 return %memory;
214}
215
216
Matt Spinler6d391252017-01-31 13:46:06 -0600217#Returns a hash that represents the 'reserved-memory' node.
218#This currently only supports the memory region for the LPC
219#host spi flash mailbox. Will look like:
220# reserved-memory {
221# #address-cells = <1>;
222# #size-cells = <1>;
223# ranges;
224#
225# flash_memory: region@94000000 {
226# no-map;
227# reg = <0x94000000 0x04000000>;
228# };
229# };
230sub getReservedMemory
231{
232 my %memory;
233
234 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
235 return %memory;
236 }
237
238 $memory{"#address-cells"} = "<1>";
239 $memory{"#size-cells"} = "<1>";
240 $memory{ranges} = ZERO_LENGTH_PROPERTY;
241
242 #Get the sub node that contains the address range
243 my ($name, $node) = getHostSpiFlashMboxRegion();
244 $memory{$name} = { %$node };
245
246 return %memory;
247}
248
249
250#Returns a hash that represents a child node of the
251#reserved-memory node which contains the address range
252#that the host spi flash is mapped to.
253sub getHostSpiFlashMboxRegion
254{
255 my %node;
256
257 $node{"no-map"} = ZERO_LENGTH_PROPERTY;
258
259 #This node needs a label the LPC node can refer to.
260 $node{NODE_LABEL} = HOST_SPI_FLASH_MEM_REGION_NODE_LABEL;
261
262 #Get the memory region's base address and size from the config file
263 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}
264 {"bmc-address-range"}{base}) {
265 die "Could not find lpc-host-spi-flash-mailbox base " .
266 "address in config file\n";
267 }
268
269 my $base = $g_configuration{"lpc-host-spi-flash-mailbox"}
270 {"bmc-address-range"}{base};
271 #Allow 1 hex value, up to 4B
272 if ($base !~ /^0x[0-9a-fA-F]{1,8}$/) {
273 die "lpc-host-spi-flash-mailbox base address $base is invalid\n";
274 }
275
276 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}
277 {"bmc-address-range"}{size}) {
278 die "Could not find lpc-host-spi-flash-mailbox address size " .
279 "in config file\n";
280 }
281
282 my $size = $g_configuration{"lpc-host-spi-flash-mailbox"}
283 {"bmc-address-range"}{size};
284 if ($size !~ /^0x[0-9a-fA-F]{1,8}$/) {
285 die "lpc-host-spi-flash-mailbox address range size " .
286 "$size is invalid\n";
287 }
288
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600289 addRegProp(\%node, $base, $size);
Matt Spinler6d391252017-01-31 13:46:06 -0600290 my $name = makeNodeName("region", $node{reg});
291
292 return ($name, \%node);
293}
294
295
Matt Spinler25d60bb2016-10-31 15:16:03 -0500296#Returns an array of hashes representing the device tree nodes for
297#the BMC flash. These nodes are BMC model specific because different
298#models can have different device drivers.
Matt Spinler889343f2017-01-30 14:14:11 -0600299sub getBMCFlashNodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500300{
Matt Spinler25d60bb2016-10-31 15:16:03 -0500301 my @nodes;
302
303 if ($g_bmcModel eq "AST2500") {
304 my %node = getAST2500BMCSPIFlashNode();
305 push @nodes, { %node };
306 }
307 else {
308 die "ERROR: No BMC SPI flash support yet for BMC model $g_bmcModel\n";
309 }
310
311 return @nodes;
312}
313
314
315#Returns a hash that represents the BMC SPI flash(es) by finding the SPI
316#connections that come from the unit tagged as BMC_CODE. The code also
317#looks in the config file for any additional properties to add. Supports
318#the hardware where the same SPI master unit can be wired to more than 1
319#flash (a chip select line is used to switch between them.) This is
320#specific to the ASPEED AST2500 hardware and device driver.
321#Will look like:
322# fmc {
323# status = "okay"
324# flash@0 {
325# ...
326# };
327# flash@1 {
328# ...
329# };
Matt Spinler889343f2017-01-30 14:14:11 -0600330sub getAST2500BMCSPIFlashNode
Matt Spinler25d60bb2016-10-31 15:16:03 -0500331{
332 my %bmcFlash;
333 my $chipSelect = 0;
334 my $lastUnit = "";
335
Matt Spinler18d5f572016-11-15 15:25:45 -0600336 my $connections = $g_targetObj->findConnections($g_bmc, "SPI", "FLASH");
Matt Spinler25d60bb2016-10-31 15:16:03 -0500337
338 if ($connections eq "") {
339 die "ERROR: No BMC SPI flashes found connected to the BMC\n";
340 }
341
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600342 statusOK(\%{$bmcFlash{fmc}});
Matt Spinler25d60bb2016-10-31 15:16:03 -0500343
344 foreach my $spi (@{$connections->{CONN}}) {
345
346 #Looking for spi-masters with a function of 'BMC_CODE'.
347 #It's possible there are multiple flash chips here.
348 if (!$g_targetObj->isBadAttribute($spi->{SOURCE}, "SPI_FUNCTION")) {
349
350 my $function = $g_targetObj->getAttribute($spi->{SOURCE},
351 "SPI_FUNCTION");
352 if ($function eq "BMC_CODE") {
353
354 my $flashName = "flash@".$chipSelect;
355
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500356 $bmcFlash{fmc}{$flashName}{COMMENT} = connectionComment($spi);
Matt Spinler25d60bb2016-10-31 15:16:03 -0500357
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600358 statusOK(\%{$bmcFlash{fmc}{$flashName}});
Matt Spinler25d60bb2016-10-31 15:16:03 -0500359
360 #Add in anything specified in the config file for this chip.
361 addBMCFlashConfigProperties(\%{$bmcFlash{fmc}{$flashName}},
362 $chipSelect);
363
364 #The code currently only supports the config where a chip
365 #select line is used to select between possibly multiple
366 #flash chips attached to the same SPI pins/unit. So we
367 #need to make sure if there are multiple chips found, that
368 #they are off of the same master unit.
369 if ($lastUnit eq "") {
370 $lastUnit = $spi->{SOURCE};
371 }
372 else {
373 if ($lastUnit ne $spi->{SOURCE}) {
374 die "ERROR: Currently only 1 spi-master unit is " .
375 "supported for BMC flash connections."
376 }
377 }
378
379 #Since we don't need anything chip select specific from the
380 #XML, we can just assign our own chip selects.
381 $chipSelect++;
382 }
383 }
384 }
385
386 if ($chipSelect == 0) {
387 die "ERROR: Didn't find any BMC flash chips connected";
388 }
389
390 return %bmcFlash;
391}
392
393
394#Looks in the bmc-flash-config section in the config file for the
395#chip select passed in to add any additional properties to the BMC
396#flash node.
397# $node = hash reference to the flash node
398# $cs = the flash chip select value
Matt Spinler889343f2017-01-30 14:14:11 -0600399sub addBMCFlashConfigProperties
Matt Spinler25d60bb2016-10-31 15:16:03 -0500400{
401 my ($node, $cs) = @_;
402 my $section = "chip-select-$cs";
403
404 if (exists $g_configuration{"bmc-flash-config"}{$section}) {
405 foreach my $key (sort keys $g_configuration{"bmc-flash-config"}{$section}) {
406 $node->{$key} = $g_configuration{"bmc-flash-config"}{$section}{$key};
407 }
408 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500409}
410
411
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500412#Returns an array of hashes representing the other flashes used by the
413#BMC besides the ones that hold the BMC code. This is BMC model specific
414#as different models can have different interfaces.
415#Typically, these are SPI flashes.
Matt Spinler889343f2017-01-30 14:14:11 -0600416sub getOtherFlashNodes
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500417{
418 my @nodes;
419
420 if ($g_bmcModel eq "AST2500") {
421 @nodes = getAST2500SpiFlashNodes();
422 }
423 else {
424 die "ERROR: No SPI flash support yet for BMC model $g_bmcModel\n";
425 }
426
427 return @nodes;
428}
429
430
431#Returns an array of hashes representing the SPI flashes in an
432#AST2500. These are for the SPI1 and SPI2 interfaces in the chip.
433#Each SPI master interface can support multiple flash chips. If
434#no hardware is connected to the interface, the node won't be present.
Matt Spinler889343f2017-01-30 14:14:11 -0600435sub getAST2500SpiFlashNodes
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500436{
437 my @nodes;
438
439 #The AST2500 has 2 SPI master units, 1 and 2.
440 my @units = (1, 2);
441
442 foreach my $unit (@units) {
443
Matt Spinler908e1822017-01-31 14:02:58 -0600444 my ($node, $foundPNOR) = getAST2500SpiMasterNode($unit);
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500445
Matt Spinler908e1822017-01-31 14:02:58 -0600446 if (keys %$node) {
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500447 my %spiNode;
448 my $nodeName = "spi$unit";
Matt Spinler908e1822017-01-31 14:02:58 -0600449 $spiNode{$nodeName} = { %$node };
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500450 push @nodes, { %spiNode };
Matt Spinler908e1822017-01-31 14:02:58 -0600451
452 #Save off the PNOR SPI node name for use by LPC node
453 if ($foundPNOR) {
454 $g_pnorNodeName = $nodeName;
455 }
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500456 }
457 }
458
459 return @nodes;
460}
461
462
463#Returns a hash that represents the device tree node for the SPI1
464#or SPI2 master interface on the AST2500. Each master can support
465#multiple chips by use of a chip select.
466#Will look like:
467# spi1 {
468# status = "okay";
469# flash@0 {
470# ...
471# };
472# };
473#
474# $spiNum = The SPI master unit number to use
Matt Spinler889343f2017-01-30 14:14:11 -0600475sub getAST2500SpiMasterNode
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500476{
477 my $spiNum = shift;
478 my %spiMaster;
479 my $chipSelect = 0;
Matt Spinler908e1822017-01-31 14:02:58 -0600480 my $foundPNOR = 0;
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500481
Matt Spinler18d5f572016-11-15 15:25:45 -0600482 my $connections = $g_targetObj->findConnections($g_bmc, "SPI", "FLASH");
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500483
484 if ($connections eq "") {
485 return %spiMaster;
486 }
487
488 #Looking for spi-masters with a chip-unit of $spiNum
489 #It's possible there are multiple flash chips off the master
490 foreach my $spi (@{$connections->{CONN}}) {
491
492 my $unitNum = $g_targetObj->getAttribute($spi->{SOURCE},
493 "CHIP_UNIT");
494 if ($unitNum == $spiNum) {
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600495 statusOK(\%spiMaster);
Matt Spinler2efdcba2016-11-08 15:37:20 -0600496
497 #Add in any pinctrl properties. These would come from the parent
498 #of $spi{SOURCE}, which would be a unit-pingroup-bmc if the
499 #pins for this connection are multi-function.
500 addPinCtrlProps($g_targetObj->getTargetParent($spi->{SOURCE}),
501 \%spiMaster);
502
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500503 my $flashName = "flash@".$chipSelect;
504
505 $spiMaster{$flashName}{COMMENT} = connectionComment($spi);
506
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600507 statusOK(\%{$spiMaster{$flashName}});
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500508
Matt Spinler0eda4882016-11-30 15:20:11 -0600509 #AST2500 PNORs need a label
510 my $function = $g_targetObj->getAttribute($spi->{SOURCE},
511 "SPI_FUNCTION");
512 if ($function eq "PNOR") {
513 $spiMaster{$flashName}{label} = "pnor";
Matt Spinler908e1822017-01-31 14:02:58 -0600514 $foundPNOR = 1;
Matt Spinler0eda4882016-11-30 15:20:11 -0600515 }
516
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500517 $chipSelect++;
518 }
519 }
520
Matt Spinler908e1822017-01-31 14:02:58 -0600521 return (\%spiMaster, $foundPNOR);
522}
523
524
525#Returns a hash that represents the mbox node.
526#This node is used by the LPC mailbox device driver.
527#Only present if the LPC mailbox is enabled in the config file.
528#Node looks like:
529# &mbox {
530# status = "okay";
531# }
532sub getMBoxNode
533{
534 my %node;
535 if (exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600536 statusOK(\%node);
Matt Spinler908e1822017-01-31 14:02:58 -0600537 }
538
539 return %node;
540}
541
542
543#Returns a hash that represents the LPC node.
544#Only present if the LPC mailbox is enabled in the config file.
545#Node looks like:
546# &lpc_ctrl {
547# flash = <&spi1>;
548# memory-region = <&flash_memory>;
549# status = "okay";
550#};
551sub getLPCNode
552{
553 my %node;
554 if (exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
555
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600556 statusOK(\%node);
Matt Spinler908e1822017-01-31 14:02:58 -0600557
558 #Point to the reserved-memory region label
559 $node{"memory-region"} = "<(ref)" .
560 HOST_SPI_FLASH_MEM_REGION_NODE_LABEL . ">";
561
562 if (not defined $g_pnorNodeName) {
563 die "The PNOR SPI flash node cannot be found but is required " .
564 "if the LPC mailbox is enabled.\n";
565 }
566
567 $node{flash} = "<(ref)$g_pnorNodeName>";
568 }
569
570 return %node;
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500571}
572
573
Matt Spinler995f2a22016-09-30 13:07:31 -0500574#Returns a hash that represents the leds node by finding all of the
575#GPIO connections to LEDs.
576#Node will look like:
577# leds {
578# <ledname> {
579# gpios = &gpio ASPEED_GPIO(x, y) GPIO_ACTIVE_xxx>
580# };
581# <another ledname> {
582# ...
583# }
Matt Spinler889343f2017-01-30 14:14:11 -0600584sub getLEDNode
Matt Spinler995f2a22016-09-30 13:07:31 -0500585{
586 my %leds;
587
Matt Spinlereca7f062016-11-07 09:59:23 -0600588 $leds{compatible} = "gpio-leds";
Matt Spinler995f2a22016-09-30 13:07:31 -0500589
Matt Spinler18d5f572016-11-15 15:25:45 -0600590 my $connections = $g_targetObj->findConnections($g_bmc, "GPIO", "LED");
Matt Spinler995f2a22016-09-30 13:07:31 -0500591
592 if ($connections eq "") {
593 print "WARNING: No LEDs found connected to the BMC\n";
594 return %leds;
595 }
596
597 foreach my $gpio (@{$connections->{CONN}}) {
598 my %ledNode;
599
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500600 $ledNode{COMMENT} = connectionComment($gpio);
Matt Spinler995f2a22016-09-30 13:07:31 -0500601
602 #The node name will be the simplified LED name
603 my $name = $gpio->{DEST_PARENT};
604 $name =~ s/(-\d+$)//; #remove trailing position
605 $name =~ s/.*\///; #remove the front of the path
606
607 #For now only supports ASPEED.
608 if (uc($g_bmcMfgr) ne "ASPEED") {
609 die "ERROR: Unsupported BMC manufacturer $g_bmcMfgr\n";
610 }
611 my $num = $g_targetObj->getAttribute($gpio->{SOURCE}, "PIN_NUM");
612 my $macro = getAspeedGpioMacro($num);
613
614 #If it's active high or low
615 my $state = $g_targetObj->getAttribute($gpio->{DEST_PARENT}, "ON_STATE");
616 my $activeString = getGpioActiveString($state);
617
618 $ledNode{gpios} = "<&gpio $macro $activeString>";
619
620 $leds{$name} = { %ledNode };
621 }
622
623 return %leds;
624}
625
626
627#Returns a either GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW
628# $val = either a 1 or a 0 for active high or low
Matt Spinler889343f2017-01-30 14:14:11 -0600629sub getGpioActiveString
630{
Matt Spinler995f2a22016-09-30 13:07:31 -0500631 my $val = shift;
632
633 if ($val == 0) {
634 return "GPIO_ACTIVE_LOW";
635 }
636
637 return "GPIO_ACTIVE_HIGH";
638}
639
640
641#Turns a GPIO number into something like ASPEED_GPIO(A, 0) for the
642#ASPEED GPIO numbering scheme A[0-7] -> Z[0-7] and then starts at
643#AA[0-7] after that.
644# $num = the GPIO number
Matt Spinler889343f2017-01-30 14:14:11 -0600645sub getAspeedGpioMacro
646{
Matt Spinler995f2a22016-09-30 13:07:31 -0500647 my $num = shift;
648 my $char;
649 my $offset = $num % 8;
650 my $block = int($num / 8);
651
652 #If past Z, wraps to AA, AB, etc
653 if ((ord('A') + $block) > ord('Z')) {
654 #how far past Z?
655 $char = $block - (ord('Z') - ord('A'));
656
657 #Don't let it wrap twice
658 if ($char > (ord('Z') - ord('A') + 1)) {
659 die "ERROR: Invalid PIN_NUM value $num found for GPIO\n";
660 }
661
662 #start back at 'A' again, and convert to a character
663 $char = chr($char + ord('A') - 1);
664
665 #Add in a bonus 'A', to get something like AB
666 $char = "A".$char;
667 }
668 else {
669 $char = ord('A') + $block;
670 $char = chr($char);
671 }
672
673 return "ASPEED_GPIO($char, $offset)";
674}
675
676
677#Returns a list of hashes that represent the UART nodes on the BMC by
678#finding the UART connections.
679#Nodes will look like:
680# &uartX {
681# status = "okay"
682# }
Matt Spinler889343f2017-01-30 14:14:11 -0600683sub getUARTNodes
Matt Spinler995f2a22016-09-30 13:07:31 -0500684{
685 my @nodes;
686
Matt Spinler23d47c22016-10-04 12:31:21 -0500687 #Using U750 for legacy MRW reasons
Matt Spinler18d5f572016-11-15 15:25:45 -0600688 my $connections = $g_targetObj->findConnections($g_bmc, "U750");
Matt Spinler995f2a22016-09-30 13:07:31 -0500689
690 if ($connections eq "") {
691 print "WARNING: No UART buses found connected to the BMC\n";
692 return @nodes;
693 }
694
695 foreach my $uart (@{$connections->{CONN}}) {
696 my %node;
697
698 my $num = $g_targetObj->getAttribute($uart->{SOURCE}, "CHIP_UNIT");
699 my $name = "uart$num";
700
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600701 statusOK(\%{$node{$name}});
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500702 $node{$name}{COMMENT} = connectionComment($uart);
Matt Spinler995f2a22016-09-30 13:07:31 -0500703
Matt Spinler2efdcba2016-11-08 15:37:20 -0600704 #Add in any pinctrl properties. These would come from the parent
705 #of $uart{SOURCE}, which would be a unit-pingroup-bmc if the
706 #pins for this connection are multi-function.
707 addPinCtrlProps($g_targetObj->getTargetParent($uart->{SOURCE}),
708 \%{$node{$name}});
709
Matt Spinler995f2a22016-09-30 13:07:31 -0500710 push @nodes, { %node };
711 }
712
Matt Spinler7d381e12016-09-27 14:27:24 -0500713 return @nodes;
714}
715
716
Matt Spinler995f2a22016-09-30 13:07:31 -0500717#Returns a list of hashes that represent the MAC (ethernet) nodes on the BMC
718#by finding the connections of type ETHERNET.
719#Nodes will look like:
720# &macX {
721# ...
722# }
Matt Spinler889343f2017-01-30 14:14:11 -0600723sub getMacNodes
Matt Spinler995f2a22016-09-30 13:07:31 -0500724{
725 my @nodes;
726
Matt Spinler18d5f572016-11-15 15:25:45 -0600727 my $connections = $g_targetObj->findConnections($g_bmc, "ETHERNET");
Matt Spinler995f2a22016-09-30 13:07:31 -0500728
729 if ($connections eq "") {
730 print "WARNING: No ethernet buses found connected to the BMC\n";
731 return @nodes;
732 }
733
734 foreach my $eth (@{$connections->{CONN}}) {
735 my %node;
736
737 my $num = $g_targetObj->getAttribute($eth->{SOURCE}, "CHIP_UNIT");
738 my $ncsi = $g_targetObj->getAttribute($eth->{SOURCE}, "NCSI_MODE");
739 my $hwChecksum = $g_targetObj->getAttribute($eth->{SOURCE},
740 "USE_HW_CHECKSUM");
741
742 my $name = "mac$num";
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600743 statusOK(\%{$node{$name}});
Matt Spinler995f2a22016-09-30 13:07:31 -0500744
745 if ($ncsi == 1) {
Matt Spinler74909132016-10-07 13:52:19 -0500746 $node{$name}{"use-ncsi"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500747 }
748 if ($hwChecksum == 0) {
Matt Spinler74909132016-10-07 13:52:19 -0500749 $node{$name}{"no-hw-checksum"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500750 }
751
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500752 $node{$name}{COMMENT} = connectionComment($eth);
Matt Spinler995f2a22016-09-30 13:07:31 -0500753
Matt Spinler2efdcba2016-11-08 15:37:20 -0600754 #Add in any pinctrl properties. These would come from the parent
755 #of $eth{SOURCE}, which would be a unit-pingroup-bmc if the
756 #pins for this connection are multi-function.
757 addPinCtrlProps($g_targetObj->getTargetParent($eth->{SOURCE}),
758 \%{$node{$name}});
759
Matt Spinler995f2a22016-09-30 13:07:31 -0500760 push @nodes, { %node };
761 }
762
763 return @nodes;
764}
765
766
767#Returns a list of hashes that represent the virtual UART nodes
768#Node will look like:
769# &vuart {
770# status = "okay"
771# }
Matt Spinler889343f2017-01-30 14:14:11 -0600772sub getVuartNodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500773{
774 my @nodes;
775 my %node;
776
777 #For now, enable 1 node all the time.
Matt Spinler995f2a22016-09-30 13:07:31 -0500778 #TBD if this needs to be fixed
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600779 statusOK(\%{$node{vuart}});
Matt Spinler7d381e12016-09-27 14:27:24 -0500780
781 push @nodes, { %node };
782
783 return @nodes;
784}
785
Matt Spinler74909132016-10-07 13:52:19 -0500786#Returns a list of hashes that represent the I2C device nodes.
787#There is 1 parent node for each bus, which then have subnodes
788#for each device on that bus. If a bus doesn't have any
789#attached devices, it doesn't need to show up.
790#The nodes will look like:
791# &i2c0 {
792# status = "okay"
793# device1@addr { (addr = 7 bit I2C address)
794# reg = <addr>
795# compatible = ...
796# ...
797# }
798# device2@addr {
799# reg = <addr>
800# ...
801# }
802# }
803# &i2c1 {
804# ...
805# }
Matt Spinler889343f2017-01-30 14:14:11 -0600806sub getI2CNodes
Matt Spinler74909132016-10-07 13:52:19 -0500807{
808 my @nodes;
809 my %busNodes;
810
Matt Spinler18d5f572016-11-15 15:25:45 -0600811 my $connections = $g_targetObj->findConnections($g_bmc, "I2C");
Matt Spinler74909132016-10-07 13:52:19 -0500812
813 if ($connections eq "") {
814 print "WARNING: No I2C buses found connected to the BMC\n";
815 return @nodes;
816 }
817
818 foreach my $i2c (@{$connections->{CONN}}) {
819
820 my %deviceNode, my $deviceName;
821
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500822 $deviceNode{COMMENT} = connectionComment($i2c);
Matt Spinler74909132016-10-07 13:52:19 -0500823
824 $deviceName = lc $i2c->{DEST_PARENT};
825 $deviceName =~ s/-\d+$//; #remove trailing position
826 $deviceName =~ s/.*\///; #remove the front of the path
827
828 #Get the I2C address
829 my $i2cAddress = $g_targetObj->getAttribute($i2c->{DEST}, "I2C_ADDRESS");
830 $i2cAddress = hex($i2cAddress);
831 if ($i2cAddress == 0) {
832 die "ERROR: Missing I2C address on $i2c->{DEST}\n";
833 }
834
835 #Put it in the format we want to print it in
836 $i2cAddress = adjustI2CAddress($i2cAddress);
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600837 addRegProp(\%deviceNode, $i2cAddress);
Matt Spinler74909132016-10-07 13:52:19 -0500838
839 $deviceName = makeNodeName($deviceName, $deviceNode{reg});
840
841 #Get the I2C bus number
842 if ($g_targetObj->isBadAttribute($i2c->{SOURCE},
843 "I2C_PORT")) {
844 die "ERROR: I2C_PORT attribute in $i2c->{DEST_PARENT} " .
845 "is either missing or empty.\n";
846 }
847
848 my $busNum = $g_targetObj->getAttribute($i2c->{SOURCE}, "I2C_PORT");
849 if ($busNum =~ /0x/i) {
850 $busNum = hex($busNum);
851 }
852
853 #Convert the number to the Linux numbering scheme.
Matt Spinler30b461c2016-10-10 16:50:07 -0500854 $busNum += $g_i2cBusAdjust;
Matt Spinler74909132016-10-07 13:52:19 -0500855
856 #Get the compatible property
857 if ($g_targetObj->isBadAttribute($i2c->{DEST_PARENT},
858 "BMC_DT_COMPATIBLE")) {
859 die "ERROR: BMC_DT_COMPATIBLE attribute in $i2c->{DEST_PARENT} " .
860 "is either missing or empty.\n";
861 }
862
863 $deviceNode{compatible} = $g_targetObj->getAttribute(
864 $i2c->{DEST_PARENT},
865 "BMC_DT_COMPATIBLE");
866
867 #Get any other part specific properties, where the property
868 #names are actually defined in the XML.
869 my %props = getPartDefinedDTProperties($i2c->{DEST_PARENT});
870 foreach my $prop (sort keys %props) {
871 $deviceNode{$prop} = $props{$prop};
872 }
873
874 #busNodeName is the hash twice so when we loop
875 #below it doesn't get lost
876 my $busNodeName = "i2c$busNum";
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600877 statusOK(\%{$busNodes{$busNodeName}{$busNodeName}});
Matt Spinler74909132016-10-07 13:52:19 -0500878 $busNodes{$busNodeName}{$busNodeName}{$deviceName} = { %deviceNode };
Matt Spinler2efdcba2016-11-08 15:37:20 -0600879
880 #Add in any pinctrl properties. These would come from the parent
881 #of $i2c{SOURCE}, which would be a unit-pingroup-bmc if the
882 #pins for this connection are multi-function.
883 addPinCtrlProps($g_targetObj->getTargetParent($i2c->{SOURCE}),
884 \%{$busNodes{$busNodeName}{$busNodeName}});
Matt Spinler74909132016-10-07 13:52:19 -0500885 }
886
887 #Each bus gets its own hash entry in the array
888 for my $b (sort keys %busNodes) {
889 push @nodes, { %{$busNodes{$b}} };
890 }
891
892 return @nodes;
893}
894
895
896#Returns a hash of property names and values that should be stored in
897#the device tree node for this device. The names of the properties and
898#the attributes to find their values in are stored in the
899#BMC_DT_ATTR_NAMES attribute in the chip.
900# $chip = the chip target
Matt Spinler889343f2017-01-30 14:14:11 -0600901sub getPartDefinedDTProperties
Matt Spinler74909132016-10-07 13:52:19 -0500902{
903 my $chip = shift;
904 my %props;
905
906 if ($g_targetObj->isBadAttribute($chip, "BMC_DT_ATTR_NAMES")) {
907 return %props;
908 }
909
910 my $attr = $g_targetObj->getAttribute($chip, "BMC_DT_ATTR_NAMES");
911 $attr =~ s/\s//g;
912 my @names = split(',', $attr);
913
914 #There can be up to 4 entries in this attribute
915 for (my $i = 0; $i < scalar @names; $i += 2) {
916
917 #$names[$i] holds the name of the attribute.
918 #$names[$i+1] holds the name of the property to store its value in.
919 if (($names[$i] ne "NA") && ($names[$i] ne "")) {
920
921 my $val = $g_targetObj->getAttribute($chip, $names[$i]);
922
923 #if the value is empty, assume it's for a standalone property,
924 #which gets turned into: some-property;
925 if ($val eq "") {
926 $props{$names[$i+1]} = ZERO_LENGTH_PROPERTY;
927 }
928 else {
929 $props{$names[$i+1]} = "<$val>";
930 }
931 }
932 }
933
934 return %props;
935}
936
937
938#Convert the MRW I2C address into the format the dts needs
939# $addr = the I2C Address
Matt Spinler889343f2017-01-30 14:14:11 -0600940sub adjustI2CAddress
Matt Spinler74909132016-10-07 13:52:19 -0500941{
942 my $addr = shift;
943
944 #MRW holds the 8 bit value. We need the 7 bit one.
Matt Spinler96f8f242016-11-28 16:26:57 -0600945 $addr = $addr >> 1;
Matt Spinler74909132016-10-07 13:52:19 -0500946 $addr = sprintf("0x%X", $addr);
947 $addr = lc $addr;
948
949 return $addr;
950}
951
952
Matt Spinler30b461c2016-10-10 16:50:07 -0500953#Sets the global $g_i2cBusAdjust from the configuration file.
Matt Spinler889343f2017-01-30 14:14:11 -0600954sub getI2CBusAdjust
Matt Spinler74909132016-10-07 13:52:19 -0500955{
Matt Spinler30b461c2016-10-10 16:50:07 -0500956 if (exists $g_configuration{"i2c-bus-adjust"}) {
Matt Spinler74909132016-10-07 13:52:19 -0500957
Matt Spinler30b461c2016-10-10 16:50:07 -0500958 $g_i2cBusAdjust = $g_configuration{"i2c-bus-adjust"};
Matt Spinler74909132016-10-07 13:52:19 -0500959
Matt Spinler30b461c2016-10-10 16:50:07 -0500960 if (!looks_like_number($g_i2cBusAdjust)) {
961 die "ERROR: Invalid i2c-bus-adjust value $g_i2cBusAdjust " .
962 "found in config file.\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500963 }
964 }
Matt Spinler30b461c2016-10-10 16:50:07 -0500965 else {
966 $g_i2cBusAdjust = 0;
967 print "WARNING: No I2C Bus number adjustment done " .
968 "for this system.\n";
969 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500970}
971
972
Matt Spinler2efdcba2016-11-08 15:37:20 -0600973
974#Adds two pinctrl properties to the device node hash passed in,
975#if specified in the MRW. Pin Control refers to a mechanism for
976#Linux to know which function of a multi-function pin to configure.
977#For example, a pin could either be configured to be a GPIO, or
978#an I2C clock line. The pin function depends on board wiring,
979#so is known by the MRW.
980# $target = the target to get the BMC_DT_PINCTRL_FUNCTS attribute from
981# $node = a hash reference to the device tree node to add the properties to
Matt Spinler889343f2017-01-30 14:14:11 -0600982sub addPinCtrlProps
Matt Spinler2efdcba2016-11-08 15:37:20 -0600983{
984 my ($target, $node) = @_;
985
986 if (!$g_targetObj->isBadAttribute($target, "BMC_DT_PINCTRL_FUNCS")) {
987 my $attr = $g_targetObj->getAttribute($target,
988 "BMC_DT_PINCTRL_FUNCS");
989
990 my $pinCtrl0Prop = makePinCtrl0PropValue($attr);
991 if ($pinCtrl0Prop ne "") {
992 $node->{"pinctrl-names"} = "default";
993 $node->{"pinctrl-0"} = $pinCtrl0Prop;
994 }
995 }
996}
997
998
999#Constructs the pinctrl-0 property value based on the
1000#BMC_DT_PINCTRL_FUNCS attribute passed in.
1001# $attr = BMC_DT_PINCTRL_FUNCS attribute value, which is an array
Matt Spinler889343f2017-01-30 14:14:11 -06001002sub makePinCtrl0PropValue
Matt Spinler2efdcba2016-11-08 15:37:20 -06001003{
1004 my $attr = shift;
1005 my @entries;
1006 my $value = "";
1007
1008 $attr =~ s/\s//g;
1009 my @funcs = split(',', $attr);
1010 foreach my $func (@funcs) {
1011 if (($func ne "NA") && ($func ne "")) {
1012 push @entries, $func;
1013 }
1014 }
1015
1016 #<&pinctrl_funcA_default &pinctrl_funcB_default ...>
1017 if (scalar @entries) {
1018 $value = "<";
1019 foreach my $entry (@entries) {
1020 $value .= "&pinctrl_".$entry."_default ";
1021 }
1022 $value =~ s/\s$//; #Remove the trailing space
1023 $value .= ">";
1024 }
1025
1026 return $value;
1027}
1028
1029
Matt Spinler7d381e12016-09-27 14:27:24 -05001030#Returns a list of compatible fields for the BMC itself.
Matt Spinler889343f2017-01-30 14:14:11 -06001031sub getBMCCompatibles
Matt Spinler7d381e12016-09-27 14:27:24 -05001032{
1033 my @compats;
1034
Matt Spinler23d47c22016-10-04 12:31:21 -05001035 #1st entry: <system mfgr>,<system name>-bmc
1036 #2nd entry: <bmc mfgr>,<bmc model>
Matt Spinler7d381e12016-09-27 14:27:24 -05001037
Matt Spinler23d47c22016-10-04 12:31:21 -05001038 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() }) {
1039 if ($g_targetObj->getType($target) eq "SYS") {
1040 my $mfgr = $g_targetObj->getAttribute($target, "MANUFACTURER");
1041 push @compats, lc "$mfgr,$g_systemName-bmc";
1042 last;
1043 }
Matt Spinler7d381e12016-09-27 14:27:24 -05001044 }
1045
1046 push @compats, lc($g_bmcMfgr).",".lc($g_bmcModel);
1047
1048 return @compats;
1049}
1050
1051
1052#Returns a string for the system's BMC model property
Matt Spinler889343f2017-01-30 14:14:11 -06001053sub getSystemBMCModel
Matt Spinler7d381e12016-09-27 14:27:24 -05001054{
Matt Spinler995f2a22016-09-30 13:07:31 -05001055 #'<System> BMC'
Matt Spinler7d381e12016-09-27 14:27:24 -05001056 my $sys = lc $g_systemName;
1057 $sys = uc(substr($sys, 0, 1)) . substr($sys, 1);
1058
1059 return $sys . " BMC";
1060}
1061
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001062#Create the comment that will show up in the device tree
1063#for a connection. In the output, will look like:
1064# // sourceUnit ->
1065# // destChip
1066#
1067# $conn = The connection hash reference
Matt Spinler889343f2017-01-30 14:14:11 -06001068sub connectionComment
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001069{
1070 my $conn = shift;
1071 my $comment = "$conn->{SOURCE} ->\n$conn->{DEST_PARENT}";
1072 return $comment;
1073}
1074
Matt Spinler7d381e12016-09-27 14:27:24 -05001075
1076#Prints a list of nodes at the same indent level
1077# $f = file handle
1078# $level = indent level (0,1,etc)
1079# @nodes = array of node hashes to print, where the
1080# key for the hash is the name of the node
Matt Spinler889343f2017-01-30 14:14:11 -06001081sub printNodes
Matt Spinler7d381e12016-09-27 14:27:24 -05001082{
1083 my ($f, $level, @nodes) = @_;
1084
1085 foreach my $n (@nodes) {
1086 my %node = %$n;
1087
1088 foreach my $name (sort keys %node) {
1089 my %n = %{ $node{$name} };
1090 printNode($f, $level, $name, %n);
1091 }
1092 }
1093}
1094
1095
1096#Print a single node and its children
1097# $f = file handle
1098# $level = indent level (0,1,etc)
1099# $name = the name of the node - shows up as:
1100# name { ...
1101# %vals = The contents of the node, with the following options:
1102# if the key is:
1103# - 'DTSI_INCLUDE', then value gets turned into a #include
Matt Spinler995f2a22016-09-30 13:07:31 -05001104# - 'COMMENT', then value gets turned into a // comment
Matt Spinler74909132016-10-07 13:52:19 -05001105# - 'ZERO_LENGTH_PROPERTY' then value gets turned into: value;
Matt Spinler7d381e12016-09-27 14:27:24 -05001106#
1107# If the value is:
1108# - a hash - then that hash gets turned into a child node
1109# where the key is the name of the child node
Matt Spinler995f2a22016-09-30 13:07:31 -05001110# - an array of hashes indicates an array of child nodes
Matt Spinler889343f2017-01-30 14:14:11 -06001111sub printNode
Matt Spinler7d381e12016-09-27 14:27:24 -05001112{
1113 my ($f, $level, $name, %vals) = @_;
1114 my $include = "";
1115
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001116 #No reason to print an empty node
1117 if (!keys %vals) {
1118 return;
1119 }
1120
Matt Spinler7d381e12016-09-27 14:27:24 -05001121 if ($level == 0) {
1122 $name = "&".$name;
1123 }
1124
Matt Spinler995f2a22016-09-30 13:07:31 -05001125 print $f "\n";
1126
1127 if (exists $vals{COMMENT}) {
1128 my @lines = split('\n', $vals{COMMENT});
1129 foreach my $l (@lines) {
1130 print $f indent($level) . "// $l\n";
1131 }
1132 }
1133
Matt Spinler6d391252017-01-31 13:46:06 -06001134 #The node can have a label, which looks like:
1135 #label : name {
1136 my $label = "";
1137 if (exists $vals{NODE_LABEL}) {
1138 $label = $vals{NODE_LABEL} . ": ";
1139 }
1140
1141 print $f indent($level) . $label . "$name {\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001142
Matt Spinler74909132016-10-07 13:52:19 -05001143 #First print properties, then includes, then subnodes
1144
1145 #Print Properties
Matt Spinler7d381e12016-09-27 14:27:24 -05001146 foreach my $v (sort keys %vals) {
1147
Matt Spinler995f2a22016-09-30 13:07:31 -05001148 next if ($v eq "COMMENT");
Matt Spinler74909132016-10-07 13:52:19 -05001149 next if ($v eq "DTSI_INCLUDE");
Matt Spinler6d391252017-01-31 13:46:06 -06001150 next if ($v eq "NODE_LABEL");
Matt Spinler74909132016-10-07 13:52:19 -05001151 next if (ref($vals{$v}) eq "HASH");
1152 next if (ref($vals{$v}) eq "ARRAY");
Matt Spinler995f2a22016-09-30 13:07:31 -05001153
Matt Spinler74909132016-10-07 13:52:19 -05001154 if ($vals{$v} ne ZERO_LENGTH_PROPERTY) {
1155 printProperty($f, $level+1, $v, $vals{$v});
Matt Spinler7d381e12016-09-27 14:27:24 -05001156 }
Matt Spinler74909132016-10-07 13:52:19 -05001157 else {
1158 printZeroLengthProperty($f, $level+1, $v);
1159 }
1160 }
1161
1162 #Print Includes
1163 foreach my $v (sort keys %vals) {
1164
1165 if ($v eq "DTSI_INCLUDE") {
1166 #print 1 include per line
1167 my @incs = split(',', $vals{$v});
1168 foreach my $i (@incs) {
Matt Spinler41dcb622017-01-31 14:55:19 -06001169 print $f qq(#include "$i"\n);
Matt Spinler74909132016-10-07 13:52:19 -05001170 }
1171 }
1172 }
1173
1174 #Print Nodes
1175 foreach my $v (sort keys %vals) {
1176
1177 if (ref($vals{$v}) eq "HASH") {
Matt Spinler7d381e12016-09-27 14:27:24 -05001178 printNode($f, $level+1, $v, %{$vals{$v}});
1179 }
Matt Spinler995f2a22016-09-30 13:07:31 -05001180 #An array of nested nodes
1181 elsif (ref($vals{$v}) eq "ARRAY") {
1182 my @array = @{$vals{$v}};
1183 &printNodes($f, $level+1, @array);
1184 }
Matt Spinler7d381e12016-09-27 14:27:24 -05001185 }
1186
1187 print $f indent($level) . "};\n";
1188}
1189
1190
1191#Prints a comma separated list of properties.
1192#e.g. a = "b, c, d";
1193# $f = file handle
1194# $level = indent level (0,1,etc)
1195# $name = name of property
1196# @vals = list of property values
Matt Spinler889343f2017-01-30 14:14:11 -06001197sub printPropertyList
Matt Spinler7d381e12016-09-27 14:27:24 -05001198{
1199 my ($f, $level, $name, @vals) = @_;
1200
1201 print $f indent($level) . "$name = ";
1202
1203 for (my $i = 0;$i < scalar @vals; $i++) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001204 print $f qq("$vals[$i]");
Matt Spinler7d381e12016-09-27 14:27:24 -05001205 if ($i < (scalar(@vals) - 1)) {
1206 print $f ", ";
1207 }
1208 }
1209 print $f ";\n"
1210}
1211
1212
1213#Prints a single property. e.g. a = "b";
1214# $f = file handle
1215# $level = indent level (0,1,etc)
1216# $name = name of property
1217# @vals = property values
Matt Spinler889343f2017-01-30 14:14:11 -06001218sub printProperty
Matt Spinler7d381e12016-09-27 14:27:24 -05001219{
1220 my ($f, $level, $name, $val) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001221 my $quoteChar = qq(");
Matt Spinler23d47c22016-10-04 12:31:21 -05001222
Matt Spinler30b461c2016-10-10 16:50:07 -05001223 $val = convertReference($val);
Matt Spinler23d47c22016-10-04 12:31:21 -05001224
1225 #properties with < > or single word aliases don't need quotes
1226 if (($val =~ /<.*>/) || ($val =~ /^&\w+$/)) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001227 $quoteChar = "";
Matt Spinler23d47c22016-10-04 12:31:21 -05001228 }
1229
Matt Spinler30b461c2016-10-10 16:50:07 -05001230 print $f indent($level) . "$name = $quoteChar$val$quoteChar;\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001231}
1232
1233
Matt Spinler30b461c2016-10-10 16:50:07 -05001234#Prints a zero length property e.g. some-property;
Matt Spinler7d381e12016-09-27 14:27:24 -05001235# $f = file handle
1236# $level = indent level (0,1,etc)
1237# $name = name of property
Matt Spinler889343f2017-01-30 14:14:11 -06001238sub printZeroLengthProperty
Matt Spinler7d381e12016-09-27 14:27:24 -05001239{
1240 my ($f, $level, $name) = @_;
1241 print $f indent($level) . "$name;\n";
1242}
1243
1244
Matt Spinler30b461c2016-10-10 16:50:07 -05001245#Replace '(ref)' with '&'.
Matt Spinler7d381e12016-09-27 14:27:24 -05001246#Needed because Serverwiz doesn't properly escape '&'s in the XML,
Matt Spinler30b461c2016-10-10 16:50:07 -05001247#so the '(ref)' string is used to represent the reference
Matt Spinler7d381e12016-09-27 14:27:24 -05001248#specifier instead of '&'.
Matt Spinler889343f2017-01-30 14:14:11 -06001249sub convertReference
1250{
Matt Spinler7d381e12016-09-27 14:27:24 -05001251 my $val = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -05001252 $val =~ s/\(ref\)/&/g;
Matt Spinler7d381e12016-09-27 14:27:24 -05001253 return $val
1254}
1255
1256
Matt Spinler7d381e12016-09-27 14:27:24 -05001257#Prints the device tree version line.
1258# $f = file handle
Matt Spinler889343f2017-01-30 14:14:11 -06001259sub printVersion
Matt Spinler7d381e12016-09-27 14:27:24 -05001260{
1261 my $f = shift;
1262 print $f VERSION."\n"
1263}
1264
1265
1266#Prints the #include line for pulling in an include file.
Matt Spinler30b461c2016-10-10 16:50:07 -05001267#The files to include come from the configuration file.
Matt Spinler7d381e12016-09-27 14:27:24 -05001268# $f = file handle
Matt Spinler30b461c2016-10-10 16:50:07 -05001269# $type = include type
Matt Spinler889343f2017-01-30 14:14:11 -06001270sub printIncludes
Matt Spinler7d381e12016-09-27 14:27:24 -05001271{
Matt Spinler30b461c2016-10-10 16:50:07 -05001272 my ($f, $type) = @_;
1273 my @includes = getIncludes($type);
Matt Spinler7d381e12016-09-27 14:27:24 -05001274
1275 foreach my $i (@includes) {
1276 #if a .dtsi, gets " ", otherwise < >
1277 if ($i =~ /\.dtsi$/) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001278 $i = qq("$i");
Matt Spinler7d381e12016-09-27 14:27:24 -05001279 }
1280 else {
Matt Spinler30b461c2016-10-10 16:50:07 -05001281 $i = "<$i>";
Matt Spinler7d381e12016-09-27 14:27:24 -05001282 }
Matt Spinler30b461c2016-10-10 16:50:07 -05001283 print $f "#include $i\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001284 }
1285}
1286
1287
Matt Spinler30b461c2016-10-10 16:50:07 -05001288#Returns an array of include files found in the config file
1289#for the type specified.
1290# $type = the include type, which is the section name in the
1291# YAML configuration file.
Matt Spinler889343f2017-01-30 14:14:11 -06001292sub getIncludes
Matt Spinler7d381e12016-09-27 14:27:24 -05001293{
Matt Spinler30b461c2016-10-10 16:50:07 -05001294 my $type = shift;
Matt Spinler7d381e12016-09-27 14:27:24 -05001295 my @includes;
1296
Matt Spinler30b461c2016-10-10 16:50:07 -05001297 #The config file may have a section but no includes
1298 #listed in it, which is OK.
1299 if ((exists $g_configuration{includes}{$type}) &&
1300 (ref($g_configuration{includes}{$type}) eq "ARRAY")) {
Matt Spinler7d381e12016-09-27 14:27:24 -05001301
Matt Spinler30b461c2016-10-10 16:50:07 -05001302 @includes = @{$g_configuration{includes}{$type}};
Matt Spinler7d381e12016-09-27 14:27:24 -05001303 }
1304
1305 return @includes;
1306}
1307
Matt Spinler30b461c2016-10-10 16:50:07 -05001308
Matt Spinler74909132016-10-07 13:52:19 -05001309#Appends the first value of the 'reg' property
1310#passed in to the name passed in to create the
1311#full name for the node
1312# $name = node name that will be appended to
1313# $reg = the reg property values
Matt Spinler889343f2017-01-30 14:14:11 -06001314sub makeNodeName
Matt Spinler74909132016-10-07 13:52:19 -05001315{
1316 my ($name, $reg) = @_;
1317
1318 $reg =~ s/<//g;
1319 $reg =~ s/>//g;
1320 my @vals = split(' ', $reg);
1321
1322 if (scalar @vals > 0) {
1323 $vals[0] =~ s/0x//;
1324 $name .= "@" . lc $vals[0];
1325 }
1326
1327 return $name;
1328}
1329
Matt Spinler7d381e12016-09-27 14:27:24 -05001330
1331#Prints the root node starting bracket.
1332# $f = file handle
Matt Spinler889343f2017-01-30 14:14:11 -06001333sub printRootNodeStart
1334{
Matt Spinler7d381e12016-09-27 14:27:24 -05001335 my $f = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -05001336 print $f qq(/ {\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001337}
1338
1339
1340#Prints the root node ending bracket.
1341# $f = file handle
1342# $level = indent level (0,1,etc)
Matt Spinler889343f2017-01-30 14:14:11 -06001343sub printRootNodeEnd
1344{
Matt Spinler7d381e12016-09-27 14:27:24 -05001345 my ($f, $level) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001346 print $f indent($level).qq(};\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001347}
1348
1349
1350#Returns a string that can be used to indent based on the
1351#level passed in. Each level is an additional 4 spaces.
1352# $level = indent level (0,1,etc)
Matt Spinler889343f2017-01-30 14:14:11 -06001353sub indent
1354{
Matt Spinler7d381e12016-09-27 14:27:24 -05001355 my $level = shift;
1356 return ' ' x ($level * 4);
1357}
1358
1359
Matt Spinler9ac5cbe2017-01-31 14:33:25 -06001360#Adds a {status} = "okay" element to the hash passed in.
1361# $node = reference to the hash to add element to
1362sub statusOK
1363{
1364 my $node = shift;
1365 $node->{status} = "okay";
1366}
1367
1368
1369#Adds the {reg} element to the hash passed in using the values
1370#passed in. Resulting value looks like: "<val1 val2 etc>"
1371# $node = reference to the hash to add element to
1372# @values = the values for the property. May be passed in one at
1373# a time and not as an array.
1374sub addRegProp
1375{
1376 my $node = shift;
1377 my @values = @_;
1378
1379 $node->{reg} = "<";
1380 for (my $i = 0; $i < scalar @values; $i++) {
1381 $node->{reg} .= $values[$i];
1382 if ($i < (scalar @values) - 1) {
1383 $node->{reg} .= " ";
1384 }
1385 }
1386 $node->{reg} .= ">";
1387}
1388
1389
Matt Spinler7d381e12016-09-27 14:27:24 -05001390sub printUsage
1391{
Matt Spinler30b461c2016-10-10 16:50:07 -05001392 print "gen_devtree.pl -x [XML filename] -y [yaml config file] " .
1393 "-o [output filename]\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001394 exit(1);
1395}