blob: 5614c4cf4e936bd75f2f372f8996ffb0357563bd [file] [log] [blame]
Matt Spinler7d381e12016-09-27 14:27:24 -05001#!/usr/bin/env perl
2
Matt Spinler5cb5f4e2017-01-31 14:17:09 -06003#Generates an OpenBMC device tree syntax file from the machine
4#readable workbook. It relies on the fact that the dts include
5#file for the BMC chip itself contains the BMC chip specific
6#data, so this can just generate the system specific data.
7#It also makes use of a YAML configuration file to contain
8#settings that are outside the scope of the MRW.
9#
10#This doesn't attempt to support every possible type of device
11#tree node from the start. Support may need to be added as newer
12#systems come along that make use of different features.
Matt Spinler7d381e12016-09-27 14:27:24 -050013
14use strict;
Matt Spinler889343f2017-01-30 14:14:11 -060015use warnings;
Matt Spinler7d381e12016-09-27 14:27:24 -050016use XML::Simple;
17use mrw::Targets;
Matt Spinlere2bf4392017-01-30 13:16:28 -060018use mrw::Util;
Matt Spinler7d381e12016-09-27 14:27:24 -050019use Getopt::Long;
Matt Spinler30b461c2016-10-10 16:50:07 -050020use YAML::Tiny qw(LoadFile);
21use Scalar::Util qw(looks_like_number);
Matt Spinler7d381e12016-09-27 14:27:24 -050022
Matt Spinler30b461c2016-10-10 16:50:07 -050023use constant {
24 VERSION => "/dts-v1/;",
25 ZERO_LENGTH_PROPERTY => "zero_length_property",
26 PRE_ROOT_INCLUDES => "pre-root-node",
27 ROOT_INCLUDES => "root-node",
Matt Spinler6d391252017-01-31 13:46:06 -060028 POST_ROOT_INCLUDES => "post-root-node",
29 HOST_SPI_FLASH_MEM_REGION_NODE_LABEL => "flash_memory"
Matt Spinler30b461c2016-10-10 16:50:07 -050030};
Matt Spinler74909132016-10-07 13:52:19 -050031
Matt Spinler7d381e12016-09-27 14:27:24 -050032
33my $serverwizFile;
Matt Spinler30b461c2016-10-10 16:50:07 -050034my $configFile;
Matt Spinler7d381e12016-09-27 14:27:24 -050035my $outputFile;
36my $debug;
37
38GetOptions("x=s" => \$serverwizFile,
Matt Spinler30b461c2016-10-10 16:50:07 -050039 "y=s" => \$configFile,
Matt Spinler7d381e12016-09-27 14:27:24 -050040 "o=s" => \$outputFile,
41 "d" => \$debug)
42or printUsage();
43
Matt Spinler30b461c2016-10-10 16:50:07 -050044if ((not defined $serverwizFile) || (not defined $outputFile) ||
45 (not defined $configFile)) {
Matt Spinler7d381e12016-09-27 14:27:24 -050046 printUsage();
47}
48
Matt Spinler908e1822017-01-31 14:02:58 -060049my $g_pnorNodeName = undef;
Matt Spinler30b461c2016-10-10 16:50:07 -050050my %g_configuration = %{ LoadFile($configFile) };
51
Matt Spinler7d381e12016-09-27 14:27:24 -050052my $g_targetObj = Targets->new;
53$g_targetObj->loadXML($serverwizFile);
54
Matt Spinler74909132016-10-07 13:52:19 -050055my ($g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName);
56setGlobalAttributes();
Matt Spinler7d381e12016-09-27 14:27:24 -050057
Matt Spinler30b461c2016-10-10 16:50:07 -050058my $g_i2cBusAdjust = 0;
59getI2CBusAdjust();
Matt Spinler7d381e12016-09-27 14:27:24 -050060
61open (my $f, ">$outputFile") or die "Could not open $outputFile\n";
62
63printVersion($f);
Matt Spinler30b461c2016-10-10 16:50:07 -050064printIncludes($f, PRE_ROOT_INCLUDES);
Matt Spinler7d381e12016-09-27 14:27:24 -050065printRootNodeStart($f);
66
67printPropertyList($f, 1, "model", getSystemBMCModel());
Matt Spinler7d381e12016-09-27 14:27:24 -050068printPropertyList($f, 1, "compatible", getBMCCompatibles());
Matt Spinler995f2a22016-09-30 13:07:31 -050069
Matt Spinler23d47c22016-10-04 12:31:21 -050070printNode($f, 1, "aliases", getAliases());
Matt Spinler7d381e12016-09-27 14:27:24 -050071printNode($f, 1, "chosen", getChosen());
Matt Spinler30b461c2016-10-10 16:50:07 -050072printNode($f, 1, "memory", getBmcMemory());
Matt Spinler6d391252017-01-31 13:46:06 -060073printNode($f, 1, "reserved-memory", getReservedMemory());
Matt Spinler7d381e12016-09-27 14:27:24 -050074
Matt Spinler995f2a22016-09-30 13:07:31 -050075printNode($f, 1, "leds", getLEDNode());
76
Matt Spinler30b461c2016-10-10 16:50:07 -050077printIncludes($f, ROOT_INCLUDES);
78
Matt Spinler7d381e12016-09-27 14:27:24 -050079printRootNodeEnd($f, 0);
80
Matt Spinler96f8f242016-11-28 16:26:57 -060081printNodes($f, 0, getBMCFlashNodes());
Matt Spinler96f8f242016-11-28 16:26:57 -060082printNodes($f, 0, getOtherFlashNodes());
83
Matt Spinler908e1822017-01-31 14:02:58 -060084printNode($f, 0, "lpc_ctrl", getLPCNode());
85printNode($f, 0, "mbox", getMBoxNode());
86
Matt Spinler995f2a22016-09-30 13:07:31 -050087printNodes($f, 0, getUARTNodes());
Matt Spinler41dcb622017-01-31 14:55:19 -060088printNodes($f, 0, getMacNodes());
89
90printNodes($f, 0, getI2CNodes());
Matt Spinler7d381e12016-09-27 14:27:24 -050091printNodes($f, 0, getVuartNodes());
92
Matt Spinler30b461c2016-10-10 16:50:07 -050093printIncludes($f, POST_ROOT_INCLUDES);
94
Matt Spinler7d381e12016-09-27 14:27:24 -050095close $f;
96exit 0;
97
98
Matt Spinler74909132016-10-07 13:52:19 -050099#Finds the values for these globals:
100# $g_bmc, $g_bmcModel, $g_bmcMfgr, $g_systemName
Matt Spinler889343f2017-01-30 14:14:11 -0600101sub setGlobalAttributes
Matt Spinler74909132016-10-07 13:52:19 -0500102{
Matt Spinlere2bf4392017-01-30 13:16:28 -0600103 $g_bmc = Util::getBMCTarget($g_targetObj);
Matt Spinler74909132016-10-07 13:52:19 -0500104
105 if ($g_targetObj->isBadAttribute($g_bmc, "MODEL")) {
106 die "The MODEL attribute on $g_bmc is missing or empty.\n";
107 }
108 $g_bmcModel = $g_targetObj->getAttribute($g_bmc, "MODEL");
109
110 if ($g_targetObj->isBadAttribute($g_bmc, "MANUFACTURER")) {
111 die "The MANUFACTURER attribute on $g_bmc is missing or empty.\n";
112 }
113 $g_bmcMfgr = $g_targetObj->getAttribute($g_bmc, "MANUFACTURER");
114
115 $g_systemName = $g_targetObj->getSystemName();
116 if (length($g_systemName) == 0) {
117 die "The SYSTEM_NAME attribute is not set on the system target.\n";
118 }
119}
120
121
Matt Spinler23d47c22016-10-04 12:31:21 -0500122#Returns a hash that represents the 'aliases' node.
123#Will look like:
124# aliases {
125# name1 = &val1;
126# name2 = &val2;
127# ...
128# }
Matt Spinler889343f2017-01-30 14:14:11 -0600129sub getAliases
Matt Spinler23d47c22016-10-04 12:31:21 -0500130{
131 my %aliases;
Matt Spinler23d47c22016-10-04 12:31:21 -0500132
Matt Spinler30b461c2016-10-10 16:50:07 -0500133 #Get the info from the config file
Matt Spinler23d47c22016-10-04 12:31:21 -0500134
Matt Spinler30b461c2016-10-10 16:50:07 -0500135 if ((not exists $g_configuration{aliases}) ||
136 (keys %{$g_configuration{aliases}} == 0)) {
137 print "WARNING: Missing or empty 'aliases' section in config file.\n";
138 return %aliases;
139 }
140 %aliases = %{ $g_configuration{aliases} };
141
142 #add a & reference if one is missing
143 foreach my $a (keys %aliases) {
144 if (($aliases{$a} !~ /^&/) && ($aliases{$a} !~ /^\(ref\)/)) {
145 $aliases{$a} = "(ref)$aliases{$a}";
Matt Spinler23d47c22016-10-04 12:31:21 -0500146 }
147 }
148
149 return %aliases;
150}
151
Matt Spinler7d381e12016-09-27 14:27:24 -0500152
153#Return a hash that represents the 'chosen' node
Matt Spinler995f2a22016-09-30 13:07:31 -0500154#Will look like:
155# chosen {
156# stdout-path = ...
157# bootargs = ...
158# }
Matt Spinler889343f2017-01-30 14:14:11 -0600159sub getChosen
Matt Spinler7d381e12016-09-27 14:27:24 -0500160{
Matt Spinler7d381e12016-09-27 14:27:24 -0500161 my %chosen;
Matt Spinler30b461c2016-10-10 16:50:07 -0500162 my @allowed = qw(bootargs stdin-path stdout-path);
163
164 #Get the info from the config file
165
166 if (not exists $g_configuration{chosen}) {
167 die "ERROR: Missing 'chosen' section in config file.\n";
168 }
169 %chosen = %{ $g_configuration{chosen} };
170
Matt Spinler30b461c2016-10-10 16:50:07 -0500171 foreach my $key (keys %chosen) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500172
Matt Spinler28fb1a92017-01-30 12:54:10 -0600173 #Check for allowed entries. Empty is OK.
174 if (!grep(/^$key$/, @allowed)) {
Matt Spinler30b461c2016-10-10 16:50:07 -0500175 die "Invalid entry $key in 'chosen' section in config file\n";
176 }
Matt Spinler28fb1a92017-01-30 12:54:10 -0600177
178 #stdout-path and stdin-path can use aliases, which will look like
179 #(alias)uart5 in the yaml. Change to (ref)uart5 so it will be
180 #converted to a '&' later.
181 $chosen{$key} =~ s/\(alias\)/\(ref\)/g;
Matt Spinler30b461c2016-10-10 16:50:07 -0500182 }
183
Matt Spinler7d381e12016-09-27 14:27:24 -0500184 return %chosen;
185}
186
187
Matt Spinler30b461c2016-10-10 16:50:07 -0500188#Return a hash that represents the 'memory' node.
Matt Spinler995f2a22016-09-30 13:07:31 -0500189#Will look like:
Matt Spinler30b461c2016-10-10 16:50:07 -0500190# memory {
191# reg = < base size >
192# }
Matt Spinler889343f2017-01-30 14:14:11 -0600193sub getBmcMemory
Matt Spinler30b461c2016-10-10 16:50:07 -0500194{
195 my %memory;
196
197 #Get the info from the config file
198
199 if (not exists $g_configuration{memory}) {
200 die "ERROR: Missing 'memory' section in config file.\n";
201 }
202
203 if ((not exists $g_configuration{memory}{base}) ||
204 ($g_configuration{memory}{base} !~ /0x/)) {
205 die "ERROR: The base entry in the memory section in the config " .
206 "file is either missing or invalid.\n";
207 }
208
209 if ((not exists $g_configuration{memory}{size}) ||
210 ($g_configuration{memory}{size} !~ /0x/)) {
211 die "ERROR: The size entry in the memory section in the config " .
212 "file is either missing or invalid.\n";
213 }
214
215 #Future: could do more validation on the actual values
216
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600217 addRegProp(\%memory,
218 $g_configuration{memory}{base},
219 $g_configuration{memory}{size});
Matt Spinler30b461c2016-10-10 16:50:07 -0500220
221 return %memory;
222}
223
224
Matt Spinler6d391252017-01-31 13:46:06 -0600225#Returns a hash that represents the 'reserved-memory' node.
226#This currently only supports the memory region for the LPC
227#host spi flash mailbox. Will look like:
228# reserved-memory {
229# #address-cells = <1>;
230# #size-cells = <1>;
231# ranges;
232#
233# flash_memory: region@94000000 {
234# no-map;
235# reg = <0x94000000 0x04000000>;
236# };
237# };
238sub getReservedMemory
239{
240 my %memory;
241
242 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
243 return %memory;
244 }
245
246 $memory{"#address-cells"} = "<1>";
247 $memory{"#size-cells"} = "<1>";
248 $memory{ranges} = ZERO_LENGTH_PROPERTY;
249
250 #Get the sub node that contains the address range
251 my ($name, $node) = getHostSpiFlashMboxRegion();
252 $memory{$name} = { %$node };
253
254 return %memory;
255}
256
257
258#Returns a hash that represents a child node of the
259#reserved-memory node which contains the address range
260#that the host spi flash is mapped to.
261sub getHostSpiFlashMboxRegion
262{
263 my %node;
264
265 $node{"no-map"} = ZERO_LENGTH_PROPERTY;
266
267 #This node needs a label the LPC node can refer to.
268 $node{NODE_LABEL} = HOST_SPI_FLASH_MEM_REGION_NODE_LABEL;
269
270 #Get the memory region's base address and size from the config file
271 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}
272 {"bmc-address-range"}{base}) {
273 die "Could not find lpc-host-spi-flash-mailbox base " .
274 "address in config file\n";
275 }
276
277 my $base = $g_configuration{"lpc-host-spi-flash-mailbox"}
278 {"bmc-address-range"}{base};
279 #Allow 1 hex value, up to 4B
280 if ($base !~ /^0x[0-9a-fA-F]{1,8}$/) {
281 die "lpc-host-spi-flash-mailbox base address $base is invalid\n";
282 }
283
284 if (not exists $g_configuration{"lpc-host-spi-flash-mailbox"}
285 {"bmc-address-range"}{size}) {
286 die "Could not find lpc-host-spi-flash-mailbox address size " .
287 "in config file\n";
288 }
289
290 my $size = $g_configuration{"lpc-host-spi-flash-mailbox"}
291 {"bmc-address-range"}{size};
292 if ($size !~ /^0x[0-9a-fA-F]{1,8}$/) {
293 die "lpc-host-spi-flash-mailbox address range size " .
294 "$size is invalid\n";
295 }
296
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600297 addRegProp(\%node, $base, $size);
Matt Spinler6d391252017-01-31 13:46:06 -0600298 my $name = makeNodeName("region", $node{reg});
299
300 return ($name, \%node);
301}
302
303
Matt Spinler25d60bb2016-10-31 15:16:03 -0500304#Returns an array of hashes representing the device tree nodes for
305#the BMC flash. These nodes are BMC model specific because different
306#models can have different device drivers.
Matt Spinler889343f2017-01-30 14:14:11 -0600307sub getBMCFlashNodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500308{
Matt Spinler25d60bb2016-10-31 15:16:03 -0500309 my @nodes;
310
311 if ($g_bmcModel eq "AST2500") {
312 my %node = getAST2500BMCSPIFlashNode();
313 push @nodes, { %node };
314 }
315 else {
316 die "ERROR: No BMC SPI flash support yet for BMC model $g_bmcModel\n";
317 }
318
319 return @nodes;
320}
321
322
323#Returns a hash that represents the BMC SPI flash(es) by finding the SPI
324#connections that come from the unit tagged as BMC_CODE. The code also
325#looks in the config file for any additional properties to add. Supports
326#the hardware where the same SPI master unit can be wired to more than 1
327#flash (a chip select line is used to switch between them.) This is
328#specific to the ASPEED AST2500 hardware and device driver.
329#Will look like:
330# fmc {
331# status = "okay"
332# flash@0 {
333# ...
334# };
335# flash@1 {
336# ...
337# };
Matt Spinler889343f2017-01-30 14:14:11 -0600338sub getAST2500BMCSPIFlashNode
Matt Spinler25d60bb2016-10-31 15:16:03 -0500339{
340 my %bmcFlash;
341 my $chipSelect = 0;
342 my $lastUnit = "";
343
Matt Spinler18d5f572016-11-15 15:25:45 -0600344 my $connections = $g_targetObj->findConnections($g_bmc, "SPI", "FLASH");
Matt Spinler25d60bb2016-10-31 15:16:03 -0500345
346 if ($connections eq "") {
347 die "ERROR: No BMC SPI flashes found connected to the BMC\n";
348 }
349
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600350 statusOK(\%{$bmcFlash{fmc}});
Matt Spinler25d60bb2016-10-31 15:16:03 -0500351
352 foreach my $spi (@{$connections->{CONN}}) {
353
354 #Looking for spi-masters with a function of 'BMC_CODE'.
355 #It's possible there are multiple flash chips here.
356 if (!$g_targetObj->isBadAttribute($spi->{SOURCE}, "SPI_FUNCTION")) {
357
358 my $function = $g_targetObj->getAttribute($spi->{SOURCE},
359 "SPI_FUNCTION");
360 if ($function eq "BMC_CODE") {
361
362 my $flashName = "flash@".$chipSelect;
363
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500364 $bmcFlash{fmc}{$flashName}{COMMENT} = connectionComment($spi);
Matt Spinler25d60bb2016-10-31 15:16:03 -0500365
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600366 statusOK(\%{$bmcFlash{fmc}{$flashName}});
Matt Spinler25d60bb2016-10-31 15:16:03 -0500367
368 #Add in anything specified in the config file for this chip.
369 addBMCFlashConfigProperties(\%{$bmcFlash{fmc}{$flashName}},
370 $chipSelect);
371
372 #The code currently only supports the config where a chip
373 #select line is used to select between possibly multiple
374 #flash chips attached to the same SPI pins/unit. So we
375 #need to make sure if there are multiple chips found, that
376 #they are off of the same master unit.
377 if ($lastUnit eq "") {
378 $lastUnit = $spi->{SOURCE};
379 }
380 else {
381 if ($lastUnit ne $spi->{SOURCE}) {
382 die "ERROR: Currently only 1 spi-master unit is " .
383 "supported for BMC flash connections."
384 }
385 }
386
387 #Since we don't need anything chip select specific from the
388 #XML, we can just assign our own chip selects.
389 $chipSelect++;
390 }
391 }
392 }
393
394 if ($chipSelect == 0) {
395 die "ERROR: Didn't find any BMC flash chips connected";
396 }
397
398 return %bmcFlash;
399}
400
401
402#Looks in the bmc-flash-config section in the config file for the
403#chip select passed in to add any additional properties to the BMC
404#flash node.
405# $node = hash reference to the flash node
406# $cs = the flash chip select value
Matt Spinler889343f2017-01-30 14:14:11 -0600407sub addBMCFlashConfigProperties
Matt Spinler25d60bb2016-10-31 15:16:03 -0500408{
409 my ($node, $cs) = @_;
410 my $section = "chip-select-$cs";
411
412 if (exists $g_configuration{"bmc-flash-config"}{$section}) {
413 foreach my $key (sort keys $g_configuration{"bmc-flash-config"}{$section}) {
414 $node->{$key} = $g_configuration{"bmc-flash-config"}{$section}{$key};
415 }
416 }
Matt Spinler995f2a22016-09-30 13:07:31 -0500417}
418
419
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500420#Returns an array of hashes representing the other flashes used by the
421#BMC besides the ones that hold the BMC code. This is BMC model specific
422#as different models can have different interfaces.
423#Typically, these are SPI flashes.
Matt Spinler889343f2017-01-30 14:14:11 -0600424sub getOtherFlashNodes
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500425{
426 my @nodes;
427
428 if ($g_bmcModel eq "AST2500") {
429 @nodes = getAST2500SpiFlashNodes();
430 }
431 else {
432 die "ERROR: No SPI flash support yet for BMC model $g_bmcModel\n";
433 }
434
435 return @nodes;
436}
437
438
439#Returns an array of hashes representing the SPI flashes in an
440#AST2500. These are for the SPI1 and SPI2 interfaces in the chip.
441#Each SPI master interface can support multiple flash chips. If
442#no hardware is connected to the interface, the node won't be present.
Matt Spinler889343f2017-01-30 14:14:11 -0600443sub getAST2500SpiFlashNodes
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500444{
445 my @nodes;
446
447 #The AST2500 has 2 SPI master units, 1 and 2.
448 my @units = (1, 2);
449
450 foreach my $unit (@units) {
451
Matt Spinler908e1822017-01-31 14:02:58 -0600452 my ($node, $foundPNOR) = getAST2500SpiMasterNode($unit);
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500453
Matt Spinler908e1822017-01-31 14:02:58 -0600454 if (keys %$node) {
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500455 my %spiNode;
456 my $nodeName = "spi$unit";
Matt Spinler908e1822017-01-31 14:02:58 -0600457 $spiNode{$nodeName} = { %$node };
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500458 push @nodes, { %spiNode };
Matt Spinler908e1822017-01-31 14:02:58 -0600459
460 #Save off the PNOR SPI node name for use by LPC node
461 if ($foundPNOR) {
462 $g_pnorNodeName = $nodeName;
463 }
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500464 }
465 }
466
467 return @nodes;
468}
469
470
471#Returns a hash that represents the device tree node for the SPI1
472#or SPI2 master interface on the AST2500. Each master can support
473#multiple chips by use of a chip select.
474#Will look like:
475# spi1 {
476# status = "okay";
477# flash@0 {
478# ...
479# };
480# };
481#
482# $spiNum = The SPI master unit number to use
Matt Spinler889343f2017-01-30 14:14:11 -0600483sub getAST2500SpiMasterNode
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500484{
485 my $spiNum = shift;
486 my %spiMaster;
487 my $chipSelect = 0;
Matt Spinler908e1822017-01-31 14:02:58 -0600488 my $foundPNOR = 0;
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500489
Matt Spinler18d5f572016-11-15 15:25:45 -0600490 my $connections = $g_targetObj->findConnections($g_bmc, "SPI", "FLASH");
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500491
492 if ($connections eq "") {
493 return %spiMaster;
494 }
495
496 #Looking for spi-masters with a chip-unit of $spiNum
497 #It's possible there are multiple flash chips off the master
498 foreach my $spi (@{$connections->{CONN}}) {
499
500 my $unitNum = $g_targetObj->getAttribute($spi->{SOURCE},
501 "CHIP_UNIT");
502 if ($unitNum == $spiNum) {
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600503 statusOK(\%spiMaster);
Matt Spinler2efdcba2016-11-08 15:37:20 -0600504
505 #Add in any pinctrl properties. These would come from the parent
506 #of $spi{SOURCE}, which would be a unit-pingroup-bmc if the
507 #pins for this connection are multi-function.
508 addPinCtrlProps($g_targetObj->getTargetParent($spi->{SOURCE}),
509 \%spiMaster);
510
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500511 my $flashName = "flash@".$chipSelect;
512
513 $spiMaster{$flashName}{COMMENT} = connectionComment($spi);
514
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600515 statusOK(\%{$spiMaster{$flashName}});
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500516
Matt Spinler0eda4882016-11-30 15:20:11 -0600517 #AST2500 PNORs need a label
518 my $function = $g_targetObj->getAttribute($spi->{SOURCE},
519 "SPI_FUNCTION");
520 if ($function eq "PNOR") {
521 $spiMaster{$flashName}{label} = "pnor";
Matt Spinler908e1822017-01-31 14:02:58 -0600522 $foundPNOR = 1;
Matt Spinler0eda4882016-11-30 15:20:11 -0600523 }
524
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500525 $chipSelect++;
526 }
527 }
528
Matt Spinler908e1822017-01-31 14:02:58 -0600529 return (\%spiMaster, $foundPNOR);
530}
531
532
533#Returns a hash that represents the mbox node.
534#This node is used by the LPC mailbox device driver.
535#Only present if the LPC mailbox is enabled in the config file.
536#Node looks like:
537# &mbox {
538# status = "okay";
539# }
540sub getMBoxNode
541{
542 my %node;
543 if (exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600544 statusOK(\%node);
Matt Spinler908e1822017-01-31 14:02:58 -0600545 }
546
547 return %node;
548}
549
550
551#Returns a hash that represents the LPC node.
552#Only present if the LPC mailbox is enabled in the config file.
553#Node looks like:
554# &lpc_ctrl {
555# flash = <&spi1>;
556# memory-region = <&flash_memory>;
557# status = "okay";
558#};
559sub getLPCNode
560{
561 my %node;
562 if (exists $g_configuration{"lpc-host-spi-flash-mailbox"}) {
563
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600564 statusOK(\%node);
Matt Spinler908e1822017-01-31 14:02:58 -0600565
566 #Point to the reserved-memory region label
567 $node{"memory-region"} = "<(ref)" .
568 HOST_SPI_FLASH_MEM_REGION_NODE_LABEL . ">";
569
570 if (not defined $g_pnorNodeName) {
571 die "The PNOR SPI flash node cannot be found but is required " .
572 "if the LPC mailbox is enabled.\n";
573 }
574
575 $node{flash} = "<(ref)$g_pnorNodeName>";
576 }
577
578 return %node;
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500579}
580
581
Matt Spinler995f2a22016-09-30 13:07:31 -0500582#Returns a hash that represents the leds node by finding all of the
583#GPIO connections to LEDs.
584#Node will look like:
585# leds {
586# <ledname> {
587# gpios = &gpio ASPEED_GPIO(x, y) GPIO_ACTIVE_xxx>
588# };
589# <another ledname> {
590# ...
591# }
Matt Spinler889343f2017-01-30 14:14:11 -0600592sub getLEDNode
Matt Spinler995f2a22016-09-30 13:07:31 -0500593{
594 my %leds;
595
Matt Spinlereca7f062016-11-07 09:59:23 -0600596 $leds{compatible} = "gpio-leds";
Matt Spinler995f2a22016-09-30 13:07:31 -0500597
Matt Spinler18d5f572016-11-15 15:25:45 -0600598 my $connections = $g_targetObj->findConnections($g_bmc, "GPIO", "LED");
Matt Spinler995f2a22016-09-30 13:07:31 -0500599
600 if ($connections eq "") {
601 print "WARNING: No LEDs found connected to the BMC\n";
602 return %leds;
603 }
604
605 foreach my $gpio (@{$connections->{CONN}}) {
606 my %ledNode;
607
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500608 $ledNode{COMMENT} = connectionComment($gpio);
Matt Spinler995f2a22016-09-30 13:07:31 -0500609
610 #The node name will be the simplified LED name
611 my $name = $gpio->{DEST_PARENT};
612 $name =~ s/(-\d+$)//; #remove trailing position
613 $name =~ s/.*\///; #remove the front of the path
614
615 #For now only supports ASPEED.
616 if (uc($g_bmcMfgr) ne "ASPEED") {
617 die "ERROR: Unsupported BMC manufacturer $g_bmcMfgr\n";
618 }
619 my $num = $g_targetObj->getAttribute($gpio->{SOURCE}, "PIN_NUM");
620 my $macro = getAspeedGpioMacro($num);
621
622 #If it's active high or low
623 my $state = $g_targetObj->getAttribute($gpio->{DEST_PARENT}, "ON_STATE");
624 my $activeString = getGpioActiveString($state);
625
626 $ledNode{gpios} = "<&gpio $macro $activeString>";
627
628 $leds{$name} = { %ledNode };
629 }
630
631 return %leds;
632}
633
634
635#Returns a either GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW
636# $val = either a 1 or a 0 for active high or low
Matt Spinler889343f2017-01-30 14:14:11 -0600637sub getGpioActiveString
638{
Matt Spinler995f2a22016-09-30 13:07:31 -0500639 my $val = shift;
640
641 if ($val == 0) {
642 return "GPIO_ACTIVE_LOW";
643 }
644
645 return "GPIO_ACTIVE_HIGH";
646}
647
648
649#Turns a GPIO number into something like ASPEED_GPIO(A, 0) for the
650#ASPEED GPIO numbering scheme A[0-7] -> Z[0-7] and then starts at
651#AA[0-7] after that.
652# $num = the GPIO number
Matt Spinler889343f2017-01-30 14:14:11 -0600653sub getAspeedGpioMacro
654{
Matt Spinler995f2a22016-09-30 13:07:31 -0500655 my $num = shift;
656 my $char;
657 my $offset = $num % 8;
658 my $block = int($num / 8);
659
660 #If past Z, wraps to AA, AB, etc
661 if ((ord('A') + $block) > ord('Z')) {
662 #how far past Z?
663 $char = $block - (ord('Z') - ord('A'));
664
665 #Don't let it wrap twice
666 if ($char > (ord('Z') - ord('A') + 1)) {
667 die "ERROR: Invalid PIN_NUM value $num found for GPIO\n";
668 }
669
670 #start back at 'A' again, and convert to a character
671 $char = chr($char + ord('A') - 1);
672
673 #Add in a bonus 'A', to get something like AB
674 $char = "A".$char;
675 }
676 else {
677 $char = ord('A') + $block;
678 $char = chr($char);
679 }
680
681 return "ASPEED_GPIO($char, $offset)";
682}
683
684
685#Returns a list of hashes that represent the UART nodes on the BMC by
686#finding the UART connections.
687#Nodes will look like:
688# &uartX {
689# status = "okay"
690# }
Matt Spinler889343f2017-01-30 14:14:11 -0600691sub getUARTNodes
Matt Spinler995f2a22016-09-30 13:07:31 -0500692{
693 my @nodes;
694
Matt Spinler23d47c22016-10-04 12:31:21 -0500695 #Using U750 for legacy MRW reasons
Matt Spinler18d5f572016-11-15 15:25:45 -0600696 my $connections = $g_targetObj->findConnections($g_bmc, "U750");
Matt Spinler995f2a22016-09-30 13:07:31 -0500697
698 if ($connections eq "") {
699 print "WARNING: No UART buses found connected to the BMC\n";
700 return @nodes;
701 }
702
703 foreach my $uart (@{$connections->{CONN}}) {
704 my %node;
705
706 my $num = $g_targetObj->getAttribute($uart->{SOURCE}, "CHIP_UNIT");
707 my $name = "uart$num";
708
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600709 statusOK(\%{$node{$name}});
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500710 $node{$name}{COMMENT} = connectionComment($uart);
Matt Spinler995f2a22016-09-30 13:07:31 -0500711
Matt Spinler2efdcba2016-11-08 15:37:20 -0600712 #Add in any pinctrl properties. These would come from the parent
713 #of $uart{SOURCE}, which would be a unit-pingroup-bmc if the
714 #pins for this connection are multi-function.
715 addPinCtrlProps($g_targetObj->getTargetParent($uart->{SOURCE}),
716 \%{$node{$name}});
717
Matt Spinler995f2a22016-09-30 13:07:31 -0500718 push @nodes, { %node };
719 }
720
Matt Spinler7d381e12016-09-27 14:27:24 -0500721 return @nodes;
722}
723
724
Matt Spinler995f2a22016-09-30 13:07:31 -0500725#Returns a list of hashes that represent the MAC (ethernet) nodes on the BMC
726#by finding the connections of type ETHERNET.
727#Nodes will look like:
728# &macX {
729# ...
730# }
Matt Spinler889343f2017-01-30 14:14:11 -0600731sub getMacNodes
Matt Spinler995f2a22016-09-30 13:07:31 -0500732{
733 my @nodes;
734
Matt Spinler18d5f572016-11-15 15:25:45 -0600735 my $connections = $g_targetObj->findConnections($g_bmc, "ETHERNET");
Matt Spinler995f2a22016-09-30 13:07:31 -0500736
737 if ($connections eq "") {
738 print "WARNING: No ethernet buses found connected to the BMC\n";
739 return @nodes;
740 }
741
742 foreach my $eth (@{$connections->{CONN}}) {
743 my %node;
744
745 my $num = $g_targetObj->getAttribute($eth->{SOURCE}, "CHIP_UNIT");
746 my $ncsi = $g_targetObj->getAttribute($eth->{SOURCE}, "NCSI_MODE");
747 my $hwChecksum = $g_targetObj->getAttribute($eth->{SOURCE},
748 "USE_HW_CHECKSUM");
749
750 my $name = "mac$num";
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600751 statusOK(\%{$node{$name}});
Matt Spinler995f2a22016-09-30 13:07:31 -0500752
753 if ($ncsi == 1) {
Matt Spinler74909132016-10-07 13:52:19 -0500754 $node{$name}{"use-ncsi"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500755 }
756 if ($hwChecksum == 0) {
Matt Spinler74909132016-10-07 13:52:19 -0500757 $node{$name}{"no-hw-checksum"} = ZERO_LENGTH_PROPERTY;
Matt Spinler995f2a22016-09-30 13:07:31 -0500758 }
759
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500760 $node{$name}{COMMENT} = connectionComment($eth);
Matt Spinler995f2a22016-09-30 13:07:31 -0500761
Matt Spinler2efdcba2016-11-08 15:37:20 -0600762 #Add in any pinctrl properties. These would come from the parent
763 #of $eth{SOURCE}, which would be a unit-pingroup-bmc if the
764 #pins for this connection are multi-function.
765 addPinCtrlProps($g_targetObj->getTargetParent($eth->{SOURCE}),
766 \%{$node{$name}});
767
Matt Spinler995f2a22016-09-30 13:07:31 -0500768 push @nodes, { %node };
769 }
770
771 return @nodes;
772}
773
774
775#Returns a list of hashes that represent the virtual UART nodes
776#Node will look like:
777# &vuart {
778# status = "okay"
779# }
Matt Spinler889343f2017-01-30 14:14:11 -0600780sub getVuartNodes
Matt Spinler7d381e12016-09-27 14:27:24 -0500781{
782 my @nodes;
783 my %node;
784
785 #For now, enable 1 node all the time.
Matt Spinler995f2a22016-09-30 13:07:31 -0500786 #TBD if this needs to be fixed
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600787 statusOK(\%{$node{vuart}});
Matt Spinler7d381e12016-09-27 14:27:24 -0500788
789 push @nodes, { %node };
790
791 return @nodes;
792}
793
Matt Spinler74909132016-10-07 13:52:19 -0500794#Returns a list of hashes that represent the I2C device nodes.
795#There is 1 parent node for each bus, which then have subnodes
796#for each device on that bus. If a bus doesn't have any
797#attached devices, it doesn't need to show up.
798#The nodes will look like:
799# &i2c0 {
800# status = "okay"
801# device1@addr { (addr = 7 bit I2C address)
802# reg = <addr>
803# compatible = ...
804# ...
805# }
806# device2@addr {
807# reg = <addr>
808# ...
809# }
810# }
811# &i2c1 {
812# ...
813# }
Matt Spinler889343f2017-01-30 14:14:11 -0600814sub getI2CNodes
Matt Spinler74909132016-10-07 13:52:19 -0500815{
816 my @nodes;
817 my %busNodes;
818
Matt Spinler18d5f572016-11-15 15:25:45 -0600819 my $connections = $g_targetObj->findConnections($g_bmc, "I2C");
Matt Spinler74909132016-10-07 13:52:19 -0500820
821 if ($connections eq "") {
822 print "WARNING: No I2C buses found connected to the BMC\n";
823 return @nodes;
824 }
825
826 foreach my $i2c (@{$connections->{CONN}}) {
827
828 my %deviceNode, my $deviceName;
829
Matt Spinlerc0dff8a2016-11-02 15:47:30 -0500830 $deviceNode{COMMENT} = connectionComment($i2c);
Matt Spinler74909132016-10-07 13:52:19 -0500831
832 $deviceName = lc $i2c->{DEST_PARENT};
833 $deviceName =~ s/-\d+$//; #remove trailing position
834 $deviceName =~ s/.*\///; #remove the front of the path
835
836 #Get the I2C address
837 my $i2cAddress = $g_targetObj->getAttribute($i2c->{DEST}, "I2C_ADDRESS");
838 $i2cAddress = hex($i2cAddress);
839 if ($i2cAddress == 0) {
840 die "ERROR: Missing I2C address on $i2c->{DEST}\n";
841 }
842
843 #Put it in the format we want to print it in
Deepak Kodihallidaa65832017-02-22 04:36:36 -0600844 $i2cAddress = Util::adjustI2CAddress($i2cAddress);
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600845 addRegProp(\%deviceNode, $i2cAddress);
Matt Spinler74909132016-10-07 13:52:19 -0500846
847 $deviceName = makeNodeName($deviceName, $deviceNode{reg});
848
849 #Get the I2C bus number
850 if ($g_targetObj->isBadAttribute($i2c->{SOURCE},
851 "I2C_PORT")) {
852 die "ERROR: I2C_PORT attribute in $i2c->{DEST_PARENT} " .
853 "is either missing or empty.\n";
854 }
855
856 my $busNum = $g_targetObj->getAttribute($i2c->{SOURCE}, "I2C_PORT");
857 if ($busNum =~ /0x/i) {
858 $busNum = hex($busNum);
859 }
860
861 #Convert the number to the Linux numbering scheme.
Matt Spinler30b461c2016-10-10 16:50:07 -0500862 $busNum += $g_i2cBusAdjust;
Matt Spinler74909132016-10-07 13:52:19 -0500863
864 #Get the compatible property
865 if ($g_targetObj->isBadAttribute($i2c->{DEST_PARENT},
866 "BMC_DT_COMPATIBLE")) {
867 die "ERROR: BMC_DT_COMPATIBLE attribute in $i2c->{DEST_PARENT} " .
868 "is either missing or empty.\n";
869 }
870
871 $deviceNode{compatible} = $g_targetObj->getAttribute(
872 $i2c->{DEST_PARENT},
873 "BMC_DT_COMPATIBLE");
874
875 #Get any other part specific properties, where the property
876 #names are actually defined in the XML.
877 my %props = getPartDefinedDTProperties($i2c->{DEST_PARENT});
878 foreach my $prop (sort keys %props) {
879 $deviceNode{$prop} = $props{$prop};
880 }
881
882 #busNodeName is the hash twice so when we loop
883 #below it doesn't get lost
884 my $busNodeName = "i2c$busNum";
Matt Spinler9ac5cbe2017-01-31 14:33:25 -0600885 statusOK(\%{$busNodes{$busNodeName}{$busNodeName}});
Matt Spinler74909132016-10-07 13:52:19 -0500886 $busNodes{$busNodeName}{$busNodeName}{$deviceName} = { %deviceNode };
Matt Spinler2efdcba2016-11-08 15:37:20 -0600887
888 #Add in any pinctrl properties. These would come from the parent
889 #of $i2c{SOURCE}, which would be a unit-pingroup-bmc if the
890 #pins for this connection are multi-function.
891 addPinCtrlProps($g_targetObj->getTargetParent($i2c->{SOURCE}),
892 \%{$busNodes{$busNodeName}{$busNodeName}});
Matt Spinler74909132016-10-07 13:52:19 -0500893 }
894
895 #Each bus gets its own hash entry in the array
896 for my $b (sort keys %busNodes) {
897 push @nodes, { %{$busNodes{$b}} };
898 }
899
900 return @nodes;
901}
902
903
904#Returns a hash of property names and values that should be stored in
905#the device tree node for this device. The names of the properties and
906#the attributes to find their values in are stored in the
907#BMC_DT_ATTR_NAMES attribute in the chip.
908# $chip = the chip target
Matt Spinler889343f2017-01-30 14:14:11 -0600909sub getPartDefinedDTProperties
Matt Spinler74909132016-10-07 13:52:19 -0500910{
911 my $chip = shift;
912 my %props;
913
914 if ($g_targetObj->isBadAttribute($chip, "BMC_DT_ATTR_NAMES")) {
915 return %props;
916 }
917
918 my $attr = $g_targetObj->getAttribute($chip, "BMC_DT_ATTR_NAMES");
919 $attr =~ s/\s//g;
920 my @names = split(',', $attr);
921
922 #There can be up to 4 entries in this attribute
923 for (my $i = 0; $i < scalar @names; $i += 2) {
924
925 #$names[$i] holds the name of the attribute.
926 #$names[$i+1] holds the name of the property to store its value in.
927 if (($names[$i] ne "NA") && ($names[$i] ne "")) {
928
929 my $val = $g_targetObj->getAttribute($chip, $names[$i]);
930
931 #if the value is empty, assume it's for a standalone property,
932 #which gets turned into: some-property;
933 if ($val eq "") {
934 $props{$names[$i+1]} = ZERO_LENGTH_PROPERTY;
935 }
936 else {
937 $props{$names[$i+1]} = "<$val>";
938 }
939 }
940 }
941
942 return %props;
943}
944
945
Matt Spinler30b461c2016-10-10 16:50:07 -0500946#Sets the global $g_i2cBusAdjust from the configuration file.
Matt Spinler889343f2017-01-30 14:14:11 -0600947sub getI2CBusAdjust
Matt Spinler74909132016-10-07 13:52:19 -0500948{
Matt Spinler30b461c2016-10-10 16:50:07 -0500949 if (exists $g_configuration{"i2c-bus-adjust"}) {
Matt Spinler74909132016-10-07 13:52:19 -0500950
Matt Spinler30b461c2016-10-10 16:50:07 -0500951 $g_i2cBusAdjust = $g_configuration{"i2c-bus-adjust"};
Matt Spinler74909132016-10-07 13:52:19 -0500952
Matt Spinler30b461c2016-10-10 16:50:07 -0500953 if (!looks_like_number($g_i2cBusAdjust)) {
954 die "ERROR: Invalid i2c-bus-adjust value $g_i2cBusAdjust " .
955 "found in config file.\n";
Matt Spinler7d381e12016-09-27 14:27:24 -0500956 }
957 }
Matt Spinler30b461c2016-10-10 16:50:07 -0500958 else {
959 $g_i2cBusAdjust = 0;
960 print "WARNING: No I2C Bus number adjustment done " .
961 "for this system.\n";
962 }
Matt Spinler7d381e12016-09-27 14:27:24 -0500963}
964
965
Matt Spinler2efdcba2016-11-08 15:37:20 -0600966
967#Adds two pinctrl properties to the device node hash passed in,
968#if specified in the MRW. Pin Control refers to a mechanism for
969#Linux to know which function of a multi-function pin to configure.
970#For example, a pin could either be configured to be a GPIO, or
971#an I2C clock line. The pin function depends on board wiring,
972#so is known by the MRW.
973# $target = the target to get the BMC_DT_PINCTRL_FUNCTS attribute from
974# $node = a hash reference to the device tree node to add the properties to
Matt Spinler889343f2017-01-30 14:14:11 -0600975sub addPinCtrlProps
Matt Spinler2efdcba2016-11-08 15:37:20 -0600976{
977 my ($target, $node) = @_;
978
979 if (!$g_targetObj->isBadAttribute($target, "BMC_DT_PINCTRL_FUNCS")) {
980 my $attr = $g_targetObj->getAttribute($target,
981 "BMC_DT_PINCTRL_FUNCS");
982
983 my $pinCtrl0Prop = makePinCtrl0PropValue($attr);
984 if ($pinCtrl0Prop ne "") {
985 $node->{"pinctrl-names"} = "default";
986 $node->{"pinctrl-0"} = $pinCtrl0Prop;
987 }
988 }
989}
990
991
992#Constructs the pinctrl-0 property value based on the
993#BMC_DT_PINCTRL_FUNCS attribute passed in.
994# $attr = BMC_DT_PINCTRL_FUNCS attribute value, which is an array
Matt Spinler889343f2017-01-30 14:14:11 -0600995sub makePinCtrl0PropValue
Matt Spinler2efdcba2016-11-08 15:37:20 -0600996{
997 my $attr = shift;
998 my @entries;
999 my $value = "";
1000
1001 $attr =~ s/\s//g;
1002 my @funcs = split(',', $attr);
1003 foreach my $func (@funcs) {
1004 if (($func ne "NA") && ($func ne "")) {
1005 push @entries, $func;
1006 }
1007 }
1008
1009 #<&pinctrl_funcA_default &pinctrl_funcB_default ...>
1010 if (scalar @entries) {
1011 $value = "<";
1012 foreach my $entry (@entries) {
1013 $value .= "&pinctrl_".$entry."_default ";
1014 }
1015 $value =~ s/\s$//; #Remove the trailing space
1016 $value .= ">";
1017 }
1018
1019 return $value;
1020}
1021
1022
Matt Spinler7d381e12016-09-27 14:27:24 -05001023#Returns a list of compatible fields for the BMC itself.
Matt Spinler889343f2017-01-30 14:14:11 -06001024sub getBMCCompatibles
Matt Spinler7d381e12016-09-27 14:27:24 -05001025{
1026 my @compats;
1027
Matt Spinler23d47c22016-10-04 12:31:21 -05001028 #1st entry: <system mfgr>,<system name>-bmc
1029 #2nd entry: <bmc mfgr>,<bmc model>
Matt Spinler7d381e12016-09-27 14:27:24 -05001030
Matt Spinler23d47c22016-10-04 12:31:21 -05001031 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() }) {
1032 if ($g_targetObj->getType($target) eq "SYS") {
1033 my $mfgr = $g_targetObj->getAttribute($target, "MANUFACTURER");
1034 push @compats, lc "$mfgr,$g_systemName-bmc";
1035 last;
1036 }
Matt Spinler7d381e12016-09-27 14:27:24 -05001037 }
1038
1039 push @compats, lc($g_bmcMfgr).",".lc($g_bmcModel);
1040
1041 return @compats;
1042}
1043
1044
1045#Returns a string for the system's BMC model property
Matt Spinler889343f2017-01-30 14:14:11 -06001046sub getSystemBMCModel
Matt Spinler7d381e12016-09-27 14:27:24 -05001047{
Matt Spinler995f2a22016-09-30 13:07:31 -05001048 #'<System> BMC'
Matt Spinler7d381e12016-09-27 14:27:24 -05001049 my $sys = lc $g_systemName;
1050 $sys = uc(substr($sys, 0, 1)) . substr($sys, 1);
1051
1052 return $sys . " BMC";
1053}
1054
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001055#Create the comment that will show up in the device tree
1056#for a connection. In the output, will look like:
1057# // sourceUnit ->
1058# // destChip
1059#
1060# $conn = The connection hash reference
Matt Spinler889343f2017-01-30 14:14:11 -06001061sub connectionComment
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001062{
1063 my $conn = shift;
1064 my $comment = "$conn->{SOURCE} ->\n$conn->{DEST_PARENT}";
1065 return $comment;
1066}
1067
Matt Spinler7d381e12016-09-27 14:27:24 -05001068
1069#Prints a list of nodes at the same indent level
1070# $f = file handle
1071# $level = indent level (0,1,etc)
1072# @nodes = array of node hashes to print, where the
1073# key for the hash is the name of the node
Matt Spinler889343f2017-01-30 14:14:11 -06001074sub printNodes
Matt Spinler7d381e12016-09-27 14:27:24 -05001075{
1076 my ($f, $level, @nodes) = @_;
1077
1078 foreach my $n (@nodes) {
1079 my %node = %$n;
1080
1081 foreach my $name (sort keys %node) {
1082 my %n = %{ $node{$name} };
1083 printNode($f, $level, $name, %n);
1084 }
1085 }
1086}
1087
1088
1089#Print a single node and its children
1090# $f = file handle
1091# $level = indent level (0,1,etc)
1092# $name = the name of the node - shows up as:
1093# name { ...
1094# %vals = The contents of the node, with the following options:
1095# if the key is:
1096# - 'DTSI_INCLUDE', then value gets turned into a #include
Matt Spinler995f2a22016-09-30 13:07:31 -05001097# - 'COMMENT', then value gets turned into a // comment
Matt Spinler74909132016-10-07 13:52:19 -05001098# - 'ZERO_LENGTH_PROPERTY' then value gets turned into: value;
Matt Spinler7d381e12016-09-27 14:27:24 -05001099#
1100# If the value is:
1101# - a hash - then that hash gets turned into a child node
1102# where the key is the name of the child node
Matt Spinler995f2a22016-09-30 13:07:31 -05001103# - an array of hashes indicates an array of child nodes
Matt Spinler889343f2017-01-30 14:14:11 -06001104sub printNode
Matt Spinler7d381e12016-09-27 14:27:24 -05001105{
1106 my ($f, $level, $name, %vals) = @_;
1107 my $include = "";
1108
Matt Spinlerc0dff8a2016-11-02 15:47:30 -05001109 #No reason to print an empty node
1110 if (!keys %vals) {
1111 return;
1112 }
1113
Matt Spinler7d381e12016-09-27 14:27:24 -05001114 if ($level == 0) {
1115 $name = "&".$name;
1116 }
1117
Matt Spinler995f2a22016-09-30 13:07:31 -05001118 print $f "\n";
1119
1120 if (exists $vals{COMMENT}) {
1121 my @lines = split('\n', $vals{COMMENT});
1122 foreach my $l (@lines) {
1123 print $f indent($level) . "// $l\n";
1124 }
1125 }
1126
Matt Spinler6d391252017-01-31 13:46:06 -06001127 #The node can have a label, which looks like:
1128 #label : name {
1129 my $label = "";
1130 if (exists $vals{NODE_LABEL}) {
1131 $label = $vals{NODE_LABEL} . ": ";
1132 }
1133
1134 print $f indent($level) . $label . "$name {\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001135
Matt Spinler74909132016-10-07 13:52:19 -05001136 #First print properties, then includes, then subnodes
1137
1138 #Print Properties
Matt Spinler7d381e12016-09-27 14:27:24 -05001139 foreach my $v (sort keys %vals) {
1140
Matt Spinler995f2a22016-09-30 13:07:31 -05001141 next if ($v eq "COMMENT");
Matt Spinler74909132016-10-07 13:52:19 -05001142 next if ($v eq "DTSI_INCLUDE");
Matt Spinler6d391252017-01-31 13:46:06 -06001143 next if ($v eq "NODE_LABEL");
Matt Spinler74909132016-10-07 13:52:19 -05001144 next if (ref($vals{$v}) eq "HASH");
1145 next if (ref($vals{$v}) eq "ARRAY");
Matt Spinler995f2a22016-09-30 13:07:31 -05001146
Matt Spinler74909132016-10-07 13:52:19 -05001147 if ($vals{$v} ne ZERO_LENGTH_PROPERTY) {
1148 printProperty($f, $level+1, $v, $vals{$v});
Matt Spinler7d381e12016-09-27 14:27:24 -05001149 }
Matt Spinler74909132016-10-07 13:52:19 -05001150 else {
1151 printZeroLengthProperty($f, $level+1, $v);
1152 }
1153 }
1154
1155 #Print Includes
1156 foreach my $v (sort keys %vals) {
1157
1158 if ($v eq "DTSI_INCLUDE") {
1159 #print 1 include per line
1160 my @incs = split(',', $vals{$v});
1161 foreach my $i (@incs) {
Matt Spinler41dcb622017-01-31 14:55:19 -06001162 print $f qq(#include "$i"\n);
Matt Spinler74909132016-10-07 13:52:19 -05001163 }
1164 }
1165 }
1166
1167 #Print Nodes
1168 foreach my $v (sort keys %vals) {
1169
1170 if (ref($vals{$v}) eq "HASH") {
Matt Spinler7d381e12016-09-27 14:27:24 -05001171 printNode($f, $level+1, $v, %{$vals{$v}});
1172 }
Matt Spinler995f2a22016-09-30 13:07:31 -05001173 #An array of nested nodes
1174 elsif (ref($vals{$v}) eq "ARRAY") {
1175 my @array = @{$vals{$v}};
1176 &printNodes($f, $level+1, @array);
1177 }
Matt Spinler7d381e12016-09-27 14:27:24 -05001178 }
1179
1180 print $f indent($level) . "};\n";
1181}
1182
1183
1184#Prints a comma separated list of properties.
1185#e.g. a = "b, c, d";
1186# $f = file handle
1187# $level = indent level (0,1,etc)
1188# $name = name of property
1189# @vals = list of property values
Matt Spinler889343f2017-01-30 14:14:11 -06001190sub printPropertyList
Matt Spinler7d381e12016-09-27 14:27:24 -05001191{
1192 my ($f, $level, $name, @vals) = @_;
1193
1194 print $f indent($level) . "$name = ";
1195
1196 for (my $i = 0;$i < scalar @vals; $i++) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001197 print $f qq("$vals[$i]");
Matt Spinler7d381e12016-09-27 14:27:24 -05001198 if ($i < (scalar(@vals) - 1)) {
1199 print $f ", ";
1200 }
1201 }
1202 print $f ";\n"
1203}
1204
1205
1206#Prints a single property. e.g. a = "b";
1207# $f = file handle
1208# $level = indent level (0,1,etc)
1209# $name = name of property
1210# @vals = property values
Matt Spinler889343f2017-01-30 14:14:11 -06001211sub printProperty
Matt Spinler7d381e12016-09-27 14:27:24 -05001212{
1213 my ($f, $level, $name, $val) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001214 my $quoteChar = qq(");
Matt Spinler23d47c22016-10-04 12:31:21 -05001215
Matt Spinler30b461c2016-10-10 16:50:07 -05001216 $val = convertReference($val);
Matt Spinler23d47c22016-10-04 12:31:21 -05001217
1218 #properties with < > or single word aliases don't need quotes
1219 if (($val =~ /<.*>/) || ($val =~ /^&\w+$/)) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001220 $quoteChar = "";
Matt Spinler23d47c22016-10-04 12:31:21 -05001221 }
1222
Matt Spinler30b461c2016-10-10 16:50:07 -05001223 print $f indent($level) . "$name = $quoteChar$val$quoteChar;\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001224}
1225
1226
Matt Spinler30b461c2016-10-10 16:50:07 -05001227#Prints a zero length property e.g. some-property;
Matt Spinler7d381e12016-09-27 14:27:24 -05001228# $f = file handle
1229# $level = indent level (0,1,etc)
1230# $name = name of property
Matt Spinler889343f2017-01-30 14:14:11 -06001231sub printZeroLengthProperty
Matt Spinler7d381e12016-09-27 14:27:24 -05001232{
1233 my ($f, $level, $name) = @_;
1234 print $f indent($level) . "$name;\n";
1235}
1236
1237
Matt Spinler30b461c2016-10-10 16:50:07 -05001238#Replace '(ref)' with '&'.
Matt Spinler7d381e12016-09-27 14:27:24 -05001239#Needed because Serverwiz doesn't properly escape '&'s in the XML,
Matt Spinler30b461c2016-10-10 16:50:07 -05001240#so the '(ref)' string is used to represent the reference
Matt Spinler7d381e12016-09-27 14:27:24 -05001241#specifier instead of '&'.
Matt Spinler889343f2017-01-30 14:14:11 -06001242sub convertReference
1243{
Matt Spinler7d381e12016-09-27 14:27:24 -05001244 my $val = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -05001245 $val =~ s/\(ref\)/&/g;
Matt Spinler7d381e12016-09-27 14:27:24 -05001246 return $val
1247}
1248
1249
Matt Spinler7d381e12016-09-27 14:27:24 -05001250#Prints the device tree version line.
1251# $f = file handle
Matt Spinler889343f2017-01-30 14:14:11 -06001252sub printVersion
Matt Spinler7d381e12016-09-27 14:27:24 -05001253{
1254 my $f = shift;
1255 print $f VERSION."\n"
1256}
1257
1258
1259#Prints the #include line for pulling in an include file.
Matt Spinler30b461c2016-10-10 16:50:07 -05001260#The files to include come from the configuration file.
Matt Spinler7d381e12016-09-27 14:27:24 -05001261# $f = file handle
Matt Spinler30b461c2016-10-10 16:50:07 -05001262# $type = include type
Matt Spinler889343f2017-01-30 14:14:11 -06001263sub printIncludes
Matt Spinler7d381e12016-09-27 14:27:24 -05001264{
Matt Spinler30b461c2016-10-10 16:50:07 -05001265 my ($f, $type) = @_;
1266 my @includes = getIncludes($type);
Matt Spinler7d381e12016-09-27 14:27:24 -05001267
1268 foreach my $i (@includes) {
1269 #if a .dtsi, gets " ", otherwise < >
1270 if ($i =~ /\.dtsi$/) {
Matt Spinler30b461c2016-10-10 16:50:07 -05001271 $i = qq("$i");
Matt Spinler7d381e12016-09-27 14:27:24 -05001272 }
1273 else {
Matt Spinler30b461c2016-10-10 16:50:07 -05001274 $i = "<$i>";
Matt Spinler7d381e12016-09-27 14:27:24 -05001275 }
Matt Spinler30b461c2016-10-10 16:50:07 -05001276 print $f "#include $i\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001277 }
1278}
1279
1280
Matt Spinler30b461c2016-10-10 16:50:07 -05001281#Returns an array of include files found in the config file
1282#for the type specified.
1283# $type = the include type, which is the section name in the
1284# YAML configuration file.
Matt Spinler889343f2017-01-30 14:14:11 -06001285sub getIncludes
Matt Spinler7d381e12016-09-27 14:27:24 -05001286{
Matt Spinler30b461c2016-10-10 16:50:07 -05001287 my $type = shift;
Matt Spinler7d381e12016-09-27 14:27:24 -05001288 my @includes;
1289
Matt Spinler30b461c2016-10-10 16:50:07 -05001290 #The config file may have a section but no includes
1291 #listed in it, which is OK.
1292 if ((exists $g_configuration{includes}{$type}) &&
1293 (ref($g_configuration{includes}{$type}) eq "ARRAY")) {
Matt Spinler7d381e12016-09-27 14:27:24 -05001294
Matt Spinler30b461c2016-10-10 16:50:07 -05001295 @includes = @{$g_configuration{includes}{$type}};
Matt Spinler7d381e12016-09-27 14:27:24 -05001296 }
1297
1298 return @includes;
1299}
1300
Matt Spinler30b461c2016-10-10 16:50:07 -05001301
Matt Spinler74909132016-10-07 13:52:19 -05001302#Appends the first value of the 'reg' property
1303#passed in to the name passed in to create the
1304#full name for the node
1305# $name = node name that will be appended to
1306# $reg = the reg property values
Matt Spinler889343f2017-01-30 14:14:11 -06001307sub makeNodeName
Matt Spinler74909132016-10-07 13:52:19 -05001308{
1309 my ($name, $reg) = @_;
1310
1311 $reg =~ s/<//g;
1312 $reg =~ s/>//g;
1313 my @vals = split(' ', $reg);
1314
1315 if (scalar @vals > 0) {
1316 $vals[0] =~ s/0x//;
1317 $name .= "@" . lc $vals[0];
1318 }
1319
1320 return $name;
1321}
1322
Matt Spinler7d381e12016-09-27 14:27:24 -05001323
1324#Prints the root node starting bracket.
1325# $f = file handle
Matt Spinler889343f2017-01-30 14:14:11 -06001326sub printRootNodeStart
1327{
Matt Spinler7d381e12016-09-27 14:27:24 -05001328 my $f = shift;
Matt Spinler30b461c2016-10-10 16:50:07 -05001329 print $f qq(/ {\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001330}
1331
1332
1333#Prints the root node ending bracket.
1334# $f = file handle
1335# $level = indent level (0,1,etc)
Matt Spinler889343f2017-01-30 14:14:11 -06001336sub printRootNodeEnd
1337{
Matt Spinler7d381e12016-09-27 14:27:24 -05001338 my ($f, $level) = @_;
Matt Spinler30b461c2016-10-10 16:50:07 -05001339 print $f indent($level).qq(};\n);
Matt Spinler7d381e12016-09-27 14:27:24 -05001340}
1341
1342
1343#Returns a string that can be used to indent based on the
1344#level passed in. Each level is an additional 4 spaces.
1345# $level = indent level (0,1,etc)
Matt Spinler889343f2017-01-30 14:14:11 -06001346sub indent
1347{
Matt Spinler7d381e12016-09-27 14:27:24 -05001348 my $level = shift;
1349 return ' ' x ($level * 4);
1350}
1351
1352
Matt Spinler9ac5cbe2017-01-31 14:33:25 -06001353#Adds a {status} = "okay" element to the hash passed in.
1354# $node = reference to the hash to add element to
1355sub statusOK
1356{
1357 my $node = shift;
1358 $node->{status} = "okay";
1359}
1360
1361
1362#Adds the {reg} element to the hash passed in using the values
1363#passed in. Resulting value looks like: "<val1 val2 etc>"
1364# $node = reference to the hash to add element to
1365# @values = the values for the property. May be passed in one at
1366# a time and not as an array.
1367sub addRegProp
1368{
1369 my $node = shift;
1370 my @values = @_;
1371
1372 $node->{reg} = "<";
1373 for (my $i = 0; $i < scalar @values; $i++) {
1374 $node->{reg} .= $values[$i];
1375 if ($i < (scalar @values) - 1) {
1376 $node->{reg} .= " ";
1377 }
1378 }
1379 $node->{reg} .= ">";
1380}
1381
1382
Matt Spinler7d381e12016-09-27 14:27:24 -05001383sub printUsage
1384{
Matt Spinler30b461c2016-10-10 16:50:07 -05001385 print "gen_devtree.pl -x [XML filename] -y [yaml config file] " .
1386 "-o [output filename]\n";
Matt Spinler7d381e12016-09-27 14:27:24 -05001387 exit(1);
1388}