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