blob: e7260aa9f1c327488ae87f575fbcc1888d32f7e3 [file] [log] [blame]
Matt Spinler1cb9ecc2020-06-02 10:48:31 -05001#! /usr/bin/perl
2
3# This script is used for generating callout lists from the MRW for devices
4# that can be accessed from the BMC. The callouts have a location code, the
5# target name (for debug), a priority, and in some cases a MRU. The script
6# supports I2C, FSI, SPI, FSI-I2C, and FSI-SPI devices. The output is a JSON
7# file organized into sections for each callout type, with keys based on the
8# type. I2c uses a bus and address, FSI uses a link, and SPI uses a bus
9# number. If FSI is combined with I2C or SPI, then the link plus the I2C/SPI
10# keys is used. Multi-hop FSI links are indicated by a dash in between the
11# links, eg "0-1".
12#
13# An example section is:
14# "FSI":
15# {
16# "5":
17# {
18# "Callouts":[
19# {
20# "Priority":"H"
21# "LocationCode": "P1-C50",
22# "MRU":"/sys-0/node-0/motherboard/proc_socket-0/module-0/power9-0",
23# "Name":"/sys-0/node-0/motherboard/cpu0"
24# },
25# {
26# "Priority":"H",
27# "LocationCode": "P1-C42",
28# "MRU":"/sys-0/node-0/motherboard/ebmc-card/BMC-0",
29# "Name":"/sys-0/node-0/motherboard/ebmc-card"
30# },
31# {
32# "Priority":"L",
33# "LocationCode": "P1",
34# "Name":"/sys-0/node-0/motherboard"
35# }
36# ],
37# "Dest":"/sys-0/node-0/motherboard-0/proc_socket-0/module-0/power9-0",
38# "Source":"/sys-0/node-0/motherboard-0/ebmc-card-connector-0/card-0/bmc-0"
39# }
40# }
41# The Name, Dest and Source entries are MRW targets and are just for debug.
42#
43# The optional --segments argument will output a JSON file of all the bus
44# segments in the system, which is what the callouts are made from.
45
46use strict;
47use warnings;
48
49use mrw::Targets;
50use mrw::Util;
51use Getopt::Long;
52use File::Basename;
53use JSON;
54
55my $mrwFile = "";
56my $outFile = "";
57my $printSegments = 0;
58
59# Not supporting priorites A, B, or C until necessary
60my %priorities = (H => 3, M => 2, L => 1);
61
62# Segment bus types
63my %busTypes = ( I2C => 1, FSIM => 1, FSICM => 1, SPI => 1 );
64
65GetOptions(
66 "m=s" => \$mrwFile,
67 "o=s" => \$outFile,
68 "segments" => \$printSegments
69)
70 or printUsage();
71
72if (($mrwFile eq "") or ($outFile eq ""))
73{
74 printUsage();
75}
76
77# Load system MRW
78my $targets = Targets->new;
79$targets->loadXML($mrwFile);
80
81# Find all single segment buses that we care about
82my %allSegments = getPathSegments();
83
84# Write the segments to a JSON file
85if ($printSegments)
86{
87 my $outDir = dirname($outFile);
88 my $segmentsFile = "$outDir/segments.json";
89
90 open(my $fh, '>', $segmentsFile) or
91 die "Could not open file '$segmentsFile' $!";
92
93 my $json = JSON->new;
94 $json->indent(1);
95 $json->canonical(1);
96 my $text = $json->encode(\%allSegments);
97 print $fh $text;
98 close $fh;
99}
100
101# Returns a hash of all the FSI, I2C, and SPI segments in the MRW
102sub getPathSegments
103{
104 my %segments;
105 foreach my $target (sort keys %{$targets->getAllTargets()})
106 {
107 my $numConnections = $targets->getNumConnections($target);
108
109 if ($numConnections == 0)
110 {
111 next;
112 }
113
114 for (my $connIndex=0;$connIndex<$numConnections;$connIndex++)
115 {
116 my $connBusObj = $targets->getConnectionBus($target, $connIndex);
117 my $busType = $connBusObj->{bus_type};
118
119 # We only care about certain bus types
120 if (not exists $busTypes{$busType})
121 {
122 next;
123 }
124
125 my $dest = $targets->getConnectionDestination($target, $connIndex);
126
127 my %segment;
128 $segment{BusType} = $busType;
129 $segment{SourceUnit} = $target;
130 $segment{SourceChip} = getParentByClass($target, "CHIP");
131 if ($segment{SourceChip} eq "")
132 {
133 die "Warning: Could not get parent chip for source $target\n";
134 }
135
136 $segment{DestUnit} = $dest;
137 $segment{DestChip} = getParentByClass($dest, "CHIP");
138
139 # If the unit's direct parent is a connector that's OK too.
140 if ($segment{DestChip} eq "")
141 {
142 my $parent = $targets->getTargetParent($dest);
143 if ($targets->getAttribute($parent, "CLASS") eq "CONNECTOR")
144 {
145 $segment{DestChip} = $parent;
146 }
147 }
148
149 if ($segment{DestChip} eq "")
150 {
151 die "Warning: Could not get parent chip for dest $dest\n";
152 }
153
154 my $fruPath = $targets->getBusAttribute(
155 $target, $connIndex, "FRU_PATH");
156
157 if (defined $fruPath)
158 {
159 $segment{FRUPath} = $fruPath;
160 my @callouts = getFRUPathCallouts($fruPath);
161 $segment{Callouts} = \@callouts;
162 }
163 else
164 {
165 $segment{FRUPath} = "";
166 my @empty;
167 $segment{Callouts} = \@empty;
168 }
169
170 if ($busType eq "I2C")
171 {
172 $segment{I2CBus} = $targets->getAttribute($target, "I2C_PORT");
173 $segment{I2CAddress} =
174 hex($targets->getAttribute($dest, "I2C_ADDRESS"));
175
176 $segment{I2CBus} = $segment{I2CBus};
177
178 # Convert to the 7 bit address that linux uses
179 $segment{I2CAddress} =
180 Util::adjustI2CAddress($segment{I2CAddress});
181 }
182 elsif ($busType eq "FSIM")
183 {
184 $segment{FSILink} =
185 hex($targets->getAttribute($target, "FSI_LINK"));
186 }
187 elsif ($busType eq "SPI")
188 {
189 $segment{SPIBus} = $targets->getAttribute($target, "SPI_PORT");
190
191 # Seems to be in HEX sometimes
192 if ($segment{SPIBus} =~ /^0x/i)
193 {
194 $segment{SPIBus} = hex($segment{SPIBus});
195 }
196 }
197
198 push @{$segments{$busType}}, { %segment };
199 }
200 }
201
202 return %segments;
203}
204
205#Breaks the FRU_PATH atttribute up into its component callouts.
206#It looks like: "H:<some target>,L:<some other target>(<MRU>)"
207#Where H/L are the priorities and can be H/M/L.
208#The MRU that is in parentheses is optional and is a chip name on that
209#FRU target.
210sub getFRUPathCallouts
211{
212 my @callouts;
213 my $fruPath = shift;
214
215 my @entries = split(',', $fruPath);
216
217 for my $entry (@entries)
218 {
219 my %callout;
220 my ($priority, $path) = split(':', $entry);
221
222 # pull the MRU out of the parentheses at the end and then
223 # remove the parentheses.
224 if ($path =~ /\(.+\)$/)
225 {
226 ($callout{MRU}) = $path =~ /\((.+)\)/;
227
228 $path =~ s/\(.+\)$//;
229 }
230
231 # check if the target we read out is valid by
232 # checking for a required attribute
233 if ($targets->isBadAttribute($path, "CLASS"))
234 {
235 die "FRU Path $path not a valid target\n";
236 }
237
238 $callout{Priority} = $priority;
239 if (not exists $priorities{$priority})
240 {
241 die "Invalid priority: '$priority' on callout $path\n";
242 }
243
244 $callout{Name} = $path;
245
246 push @callouts, \%callout;
247 }
248
249 return @callouts;
250}
251
252# Returns an ancestor target based on its class
253sub getParentByClass
254{
255 my ($target, $class) = @_;
256 my $parent = $targets->getTargetParent($target);
257
258 while (defined $parent)
259 {
260 if (!$targets->isBadAttribute($parent, "CLASS"))
261 {
262 if ($class eq $targets->getAttribute($parent, "CLASS"))
263 {
264 return $parent;
265 }
266 }
267 $parent = $targets->getTargetParent($parent);
268 }
269
270 return "";
271}
272
273sub printUsage
274{
275 print "$0 -m <MRW file> -o <Output filename> [--segments] [-n]\n" .
276 " -m <MRW file> = The MRW XML\n" .
277 " -o <Output filename> = The output JSON\n" .
278 " [--segments] = Optionally create a segments.json file\n" .
279 exit(1);
280}