blob: b784997fa024fea0fb64e017dd08534835fa695d [file] [log] [blame]
Matt Spinler8df7be82017-01-09 15:42:05 -06001#!/usr/bin/env perl
2
3#Creates a configuration file for each hwmon sensor in the MRW
Matt Spinler268ec612017-01-09 15:49:53 -06004#for use by the phosphor-hwmon daemon. These configuration files
5#contain labels and thresholds for the hwmon features for that sensor.
6#The files are created in subdirectories based on their device
7#tree paths.
Matt Spinler8df7be82017-01-09 15:42:05 -06008
9use strict;
10use warnings;
11
12use mrw::Targets;
13use mrw::Util;
14use Getopt::Long;
Matt Spinler268ec612017-01-09 15:49:53 -060015use File::Path qw(make_path);
Matt Spinler8df7be82017-01-09 15:42:05 -060016
17use constant {
18 I2C_TYPE => "i2c"
19};
20
21my $serverwizFile;
Matt Spinler9142da42017-03-29 09:55:58 -050022my $g_outputDir;
Matt Spinler8df7be82017-01-09 15:42:05 -060023my @hwmon;
24
Matt Spinler9142da42017-03-29 09:55:58 -050025GetOptions("x=s" => \$serverwizFile,
26 "d=s" => \$g_outputDir) or printUsage();
Matt Spinler8df7be82017-01-09 15:42:05 -060027
28if (not defined $serverwizFile) {
29 printUsage();
30}
31
32my $g_targetObj = Targets->new;
33$g_targetObj->loadXML($serverwizFile);
34
35my $bmc = Util::getBMCTarget($g_targetObj);
36
37getI2CSensors($bmc, \@hwmon);
38
Matt Spinler268ec612017-01-09 15:49:53 -060039makeConfFiles($bmc, \@hwmon);
40
Matt Spinler8df7be82017-01-09 15:42:05 -060041exit 0;
42
43
44#Returns an array of hashes that represent hwmon enabled I2C sensors.
45sub getI2CSensors
46{
47 my ($bmc, $hwmon) = @_;
48 my $connections = $g_targetObj->findConnections($bmc, "I2C");
49
50 return if ($connections eq "");
51
52 for my $i2c (@{$connections->{CONN}}) {
53
54 my $chip = $i2c->{DEST_PARENT};
55 my @hwmonUnits = Util::getChildUnitsWithTargetType($g_targetObj,
56 "unit-hwmon-feature",
57 $chip);
58
Matt Spinlere3e0b722017-02-08 11:42:51 -060059 #If the MRW doesn't specify a label for a particular hwmon
60 #feature, then we don't want to use it.
61 removeUnusedHwmons(\@hwmonUnits);
62
Matt Spinler8df7be82017-01-09 15:42:05 -060063 #If chip didn't have hwmon units, it isn't hwmon enabled.
64 next unless (scalar @hwmonUnits > 0);
65
66 my %entry;
67 $entry{type} = I2C_TYPE;
68 $entry{name} = lc $g_targetObj->getInstanceName($chip);
Matt Spinlerc79b21f2017-01-09 15:45:14 -060069 getHwmonAttributes(\@hwmonUnits, \%entry);
70 getI2CAttributes($i2c, \%entry);
Matt Spinler8df7be82017-01-09 15:42:05 -060071
72 push @$hwmon, { %entry };
73 }
74}
75
76
Matt Spinlere3e0b722017-02-08 11:42:51 -060077#Removes entries from the list of hwmon units passed in that have
78#an empty HWMON_NAME or DESCRIPTIVE_NAME attribute.
79sub removeUnusedHwmons
80{
81 my ($units) = @_;
82 my $i = 0;
83
84 while ($i <= $#$units) {
85
86 my $hwmon = $g_targetObj->getAttributeField($$units[$i],
87 "HWMON_FEATURE",
88 "HWMON_NAME");
89 my $name = $g_targetObj->getAttributeField($$units[$i],
90 "HWMON_FEATURE",
91 "DESCRIPTIVE_NAME");
92 if (($hwmon eq "") || ($name eq "")) {
93 splice(@$units, $i, 1);
94 }
95 else {
96 $i++;
97 }
98 }
99}
100
101
Matt Spinlerc79b21f2017-01-09 15:45:14 -0600102#Reads the hwmon related attributes from the HWMON_FEATURE
103#complex attribute and adds them to the hash.
104sub getHwmonAttributes
105{
106 my ($units, $entry) = @_;
107 my %hwmonFeatures;
108
109 for my $unit (@$units) {
110
111 #The hwmon name, like 'in1', 'temp1', 'fan1', etc
112 my $hwmon = $g_targetObj->getAttributeField($unit,
113 "HWMON_FEATURE",
114 "HWMON_NAME");
115
116 #The useful name for this feature, like 'ambient'
117 my $name = $g_targetObj->getAttributeField($unit,
118 "HWMON_FEATURE",
119 "DESCRIPTIVE_NAME");
120 $hwmonFeatures{$hwmon}{label} = $name;
121
122 #Thresholds are optional, ignore if NA
123 my $warnHigh = $g_targetObj->getAttributeField($unit,
124 "HWMON_FEATURE",
125 "WARN_HIGH");
126 if (($warnHigh ne "") && ($warnHigh ne "NA")) {
127 $hwmonFeatures{$hwmon}{warnhigh} = $warnHigh;
128 }
129
130 my $warnLow = $g_targetObj->getAttributeField($unit,
131 "HWMON_FEATURE",
132 "WARN_LOW");
133 if (($warnLow ne "") && ($warnLow ne "NA")) {
134 $hwmonFeatures{$hwmon}{warnlow} = $warnLow;
135 }
136
137 my $critHigh = $g_targetObj->getAttributeField($unit,
138 "HWMON_FEATURE",
139 "CRIT_HIGH");
140 if (($critHigh ne "") && ($critHigh ne "NA")) {
141 $hwmonFeatures{$hwmon}{crithigh} = $critHigh;
142 }
143
144 my $critLow = $g_targetObj->getAttributeField($unit,
145 "HWMON_FEATURE",
146 "CRIT_LOW");
147 if (($critLow ne "") && ($critHigh ne "NA")) {
148 $hwmonFeatures{$hwmon}{critlow} = $critLow;
149 }
150 }
151
152 $entry->{hwmon} = { %hwmonFeatures };
153}
154
155
Matt Spinler8df7be82017-01-09 15:42:05 -0600156#Reads the I2C attributes for the chip and adds them to the hash.
157#This includes the i2C address, and register base address and
158#offset for the I2C bus the chip is on.
159sub getI2CAttributes
160{
161 my ($i2c, $entry) = @_;
162
163 #The address comes from the destination unit, and needs
164 #to be the 7 bit value in hex without the 0x.
165 my $addr = $g_targetObj->getAttribute($i2c->{DEST}, "I2C_ADDRESS");
166 $addr = hex($addr) >> 1;
167 $entry->{addr} = sprintf("%x", $addr);
168
169 #The reg base address and offset may be optional depending on
170 #the BMC chip type. We'll check later if it's required but missing.
171 if (!$g_targetObj->isBadAttribute($i2c->{SOURCE}, "REG_BASE_ADDRESS")) {
172 my $addr = $g_targetObj->getAttribute($i2c->{SOURCE},
173 "REG_BASE_ADDRESS");
174 $entry->{regBaseAddress} = sprintf("%x", hex($addr));
175 }
176
177 if (!$g_targetObj->isBadAttribute($i2c->{SOURCE}, "REG_OFFSET")) {
178 my $offset = $g_targetObj->getAttribute($i2c->{SOURCE},
179 "REG_OFFSET");
180 $entry->{regOffset} = sprintf("%x", hex($offset));
181 }
182}
183
184
Matt Spinler268ec612017-01-09 15:49:53 -0600185#Creates .conf files for each chip.
186sub makeConfFiles
187{
188 my ($bmc, $hwmon) = @_;
189
190 for my $entry (@$hwmon) {
191 printConfFile($bmc, $entry);
192 }
193}
194
195
196#Writes out a configuration file for a hwmon sensor, containing:
197# LABEL_<feature> = <descriptive label> (e.g. LABEL_temp1 = ambient)
198# WARNHI_<feature> = <value> (e.g. WARNHI_temp1 = 99)
199# WARNLO_<feature> = <value> (e.g. WARNLO_temp1 = 0)
200# CRITHI_<feature> = <value> (e.g. CRITHI_temp1 = 100)
201# CRITHI_<feature> = <value> (e.g. CRITLO_temp1 = -1)
202#
203# The file is created in a subdirectory based on the chip's device
204# tree path.
205sub printConfFile
206{
207 my ($bmc, $entry) = @_;
208 my $path = getConfFilePath($bmc, $entry);
209 my $name = $path . "/" . getConfFileName($entry);
210
211 make_path($path);
212
213 open(my $f, ">$name") or die "Could not open $name\n";
214
215 for my $feature (sort keys %{$entry->{hwmon}}) {
216 print $f "LABEL_$feature = \"$entry->{hwmon}{$feature}{label}\"\n";
217
218 #Thresholds are optional
219 if (exists $entry->{hwmon}{$feature}{warnhigh}) {
220 print $f "WARNHI_$feature = \"$entry->{hwmon}{$feature}{warnhigh}\"\n";
221 }
222 if (exists $entry->{hwmon}{$feature}{warnlow}) {
223 print $f "WARNLO_$feature = \"$entry->{hwmon}{$feature}{warnlow}\"\n";
224 }
225 if (exists $entry->{hwmon}{$feature}{crithigh}) {
226 print $f "CRITHI_$feature = \"$entry->{hwmon}{$feature}{crithigh}\"\n";
227 }
228 if (exists $entry->{hwmon}{$feature}{critlow}) {
229 print $f "CRITLO_$feature = \"$entry->{hwmon}{$feature}{critlow}\"\n";
230 }
231 }
232
233 close $f;
234}
235
236
237#Returns the chip's configuration file path.
238sub getConfFilePath
239{
240 my ($bmc, $entry) = @_;
241
242 my $mfgr = $g_targetObj->getAttribute($bmc, "MANUFACTURER");
243
244 #Unfortunately, because the conf file path is based on the
245 #device tree path which is tied to the internal chip structure,
246 #this has to be model specific. Until proven wrong, I'm going
247 #to make an assumption that all ASPEED chips have the same path
248 #as so far all of the models I've seen do.
249 if ($mfgr eq "ASPEED") {
250 return getAspeedConfFilePath($entry);
251 }
252 else {
253 die "Unsupported BMC manufacturer $mfgr\n";
254 }
255}
256
257
258#Returns the relative path of the configuration file to create.
259#This path is based on the path of the chip in the device tree.
260#An example path is ahb/apb/i2c@1e78a000/i2c-bus@400/
261sub getAspeedConfFilePath
262{
263 my ($entry) = @_;
264 my $path;
265
266 if ($entry->{type} eq I2C_TYPE) {
267
268 #ASPEED requires the reg base address & offset fields
269 if ((not exists $entry->{regBaseAddress}) ||
270 (not exists $entry->{regOffset})) {
271 die "Missing regBaseAddress or regOffset attributes " .
272 "in the I2C master unit XML\n";
273 }
274
Matt Spinler9142da42017-03-29 09:55:58 -0500275 $path = "$g_outputDir/ahb/apb/i2c\@$entry->{regBaseAddress}/i2c-bus@" .
Matt Spinler268ec612017-01-09 15:49:53 -0600276 "$entry->{regOffset}";
277 }
278 else {
279 #TODO: FSI support for the OCC when known
280 die "HWMON bus type $entry->{type} not implemented yet\n";
281 }
282
283 return $path;
284}
285
286
287#Returns the name to use for the conf file:
288# <name>@<addr>.conf (e.g. rtc@68.conf)
289sub getConfFileName
290{
291 my ($entry) = @_;
292 return "$entry->{name}\@$entry->{addr}.conf";
293}
294
295
Matt Spinler8df7be82017-01-09 15:42:05 -0600296sub printUsage
297{
Matt Spinler9142da42017-03-29 09:55:58 -0500298 print "$0 -x [XML filename] -d [output base directory]\n";
Matt Spinler8df7be82017-01-09 15:42:05 -0600299 exit(1);
300}