blob: a708a895c810a0f31934d064e78cb41b9da1aa59 [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
57 #If chip didn't have hwmon units, it isn't hwmon enabled.
58 next unless (scalar @hwmonUnits > 0);
59
60 my %entry;
61 $entry{type} = I2C_TYPE;
62 $entry{name} = lc $g_targetObj->getInstanceName($chip);
Matt Spinlerc79b21f2017-01-09 15:45:14 -060063 getHwmonAttributes(\@hwmonUnits, \%entry);
64 getI2CAttributes($i2c, \%entry);
Matt Spinler8df7be82017-01-09 15:42:05 -060065
66 push @$hwmon, { %entry };
67 }
68}
69
70
Matt Spinlerc79b21f2017-01-09 15:45:14 -060071#Reads the hwmon related attributes from the HWMON_FEATURE
72#complex attribute and adds them to the hash.
73sub getHwmonAttributes
74{
75 my ($units, $entry) = @_;
76 my %hwmonFeatures;
77
78 for my $unit (@$units) {
79
80 #The hwmon name, like 'in1', 'temp1', 'fan1', etc
81 my $hwmon = $g_targetObj->getAttributeField($unit,
82 "HWMON_FEATURE",
83 "HWMON_NAME");
84
85 #The useful name for this feature, like 'ambient'
86 my $name = $g_targetObj->getAttributeField($unit,
87 "HWMON_FEATURE",
88 "DESCRIPTIVE_NAME");
89 $hwmonFeatures{$hwmon}{label} = $name;
90
91 #Thresholds are optional, ignore if NA
92 my $warnHigh = $g_targetObj->getAttributeField($unit,
93 "HWMON_FEATURE",
94 "WARN_HIGH");
95 if (($warnHigh ne "") && ($warnHigh ne "NA")) {
96 $hwmonFeatures{$hwmon}{warnhigh} = $warnHigh;
97 }
98
99 my $warnLow = $g_targetObj->getAttributeField($unit,
100 "HWMON_FEATURE",
101 "WARN_LOW");
102 if (($warnLow ne "") && ($warnLow ne "NA")) {
103 $hwmonFeatures{$hwmon}{warnlow} = $warnLow;
104 }
105
106 my $critHigh = $g_targetObj->getAttributeField($unit,
107 "HWMON_FEATURE",
108 "CRIT_HIGH");
109 if (($critHigh ne "") && ($critHigh ne "NA")) {
110 $hwmonFeatures{$hwmon}{crithigh} = $critHigh;
111 }
112
113 my $critLow = $g_targetObj->getAttributeField($unit,
114 "HWMON_FEATURE",
115 "CRIT_LOW");
116 if (($critLow ne "") && ($critHigh ne "NA")) {
117 $hwmonFeatures{$hwmon}{critlow} = $critLow;
118 }
119 }
120
121 $entry->{hwmon} = { %hwmonFeatures };
122}
123
124
Matt Spinler8df7be82017-01-09 15:42:05 -0600125#Reads the I2C attributes for the chip and adds them to the hash.
126#This includes the i2C address, and register base address and
127#offset for the I2C bus the chip is on.
128sub getI2CAttributes
129{
130 my ($i2c, $entry) = @_;
131
132 #The address comes from the destination unit, and needs
133 #to be the 7 bit value in hex without the 0x.
134 my $addr = $g_targetObj->getAttribute($i2c->{DEST}, "I2C_ADDRESS");
135 $addr = hex($addr) >> 1;
136 $entry->{addr} = sprintf("%x", $addr);
137
138 #The reg base address and offset may be optional depending on
139 #the BMC chip type. We'll check later if it's required but missing.
140 if (!$g_targetObj->isBadAttribute($i2c->{SOURCE}, "REG_BASE_ADDRESS")) {
141 my $addr = $g_targetObj->getAttribute($i2c->{SOURCE},
142 "REG_BASE_ADDRESS");
143 $entry->{regBaseAddress} = sprintf("%x", hex($addr));
144 }
145
146 if (!$g_targetObj->isBadAttribute($i2c->{SOURCE}, "REG_OFFSET")) {
147 my $offset = $g_targetObj->getAttribute($i2c->{SOURCE},
148 "REG_OFFSET");
149 $entry->{regOffset} = sprintf("%x", hex($offset));
150 }
151}
152
153
Matt Spinler268ec612017-01-09 15:49:53 -0600154#Creates .conf files for each chip.
155sub makeConfFiles
156{
157 my ($bmc, $hwmon) = @_;
158
159 for my $entry (@$hwmon) {
160 printConfFile($bmc, $entry);
161 }
162}
163
164
165#Writes out a configuration file for a hwmon sensor, containing:
166# LABEL_<feature> = <descriptive label> (e.g. LABEL_temp1 = ambient)
167# WARNHI_<feature> = <value> (e.g. WARNHI_temp1 = 99)
168# WARNLO_<feature> = <value> (e.g. WARNLO_temp1 = 0)
169# CRITHI_<feature> = <value> (e.g. CRITHI_temp1 = 100)
170# CRITHI_<feature> = <value> (e.g. CRITLO_temp1 = -1)
171#
172# The file is created in a subdirectory based on the chip's device
173# tree path.
174sub printConfFile
175{
176 my ($bmc, $entry) = @_;
177 my $path = getConfFilePath($bmc, $entry);
178 my $name = $path . "/" . getConfFileName($entry);
179
180 make_path($path);
181
182 open(my $f, ">$name") or die "Could not open $name\n";
183
184 for my $feature (sort keys %{$entry->{hwmon}}) {
185 print $f "LABEL_$feature = \"$entry->{hwmon}{$feature}{label}\"\n";
186
187 #Thresholds are optional
188 if (exists $entry->{hwmon}{$feature}{warnhigh}) {
189 print $f "WARNHI_$feature = \"$entry->{hwmon}{$feature}{warnhigh}\"\n";
190 }
191 if (exists $entry->{hwmon}{$feature}{warnlow}) {
192 print $f "WARNLO_$feature = \"$entry->{hwmon}{$feature}{warnlow}\"\n";
193 }
194 if (exists $entry->{hwmon}{$feature}{crithigh}) {
195 print $f "CRITHI_$feature = \"$entry->{hwmon}{$feature}{crithigh}\"\n";
196 }
197 if (exists $entry->{hwmon}{$feature}{critlow}) {
198 print $f "CRITLO_$feature = \"$entry->{hwmon}{$feature}{critlow}\"\n";
199 }
200 }
201
202 close $f;
203}
204
205
206#Returns the chip's configuration file path.
207sub getConfFilePath
208{
209 my ($bmc, $entry) = @_;
210
211 my $mfgr = $g_targetObj->getAttribute($bmc, "MANUFACTURER");
212
213 #Unfortunately, because the conf file path is based on the
214 #device tree path which is tied to the internal chip structure,
215 #this has to be model specific. Until proven wrong, I'm going
216 #to make an assumption that all ASPEED chips have the same path
217 #as so far all of the models I've seen do.
218 if ($mfgr eq "ASPEED") {
219 return getAspeedConfFilePath($entry);
220 }
221 else {
222 die "Unsupported BMC manufacturer $mfgr\n";
223 }
224}
225
226
227#Returns the relative path of the configuration file to create.
228#This path is based on the path of the chip in the device tree.
229#An example path is ahb/apb/i2c@1e78a000/i2c-bus@400/
230sub getAspeedConfFilePath
231{
232 my ($entry) = @_;
233 my $path;
234
235 if ($entry->{type} eq I2C_TYPE) {
236
237 #ASPEED requires the reg base address & offset fields
238 if ((not exists $entry->{regBaseAddress}) ||
239 (not exists $entry->{regOffset})) {
240 die "Missing regBaseAddress or regOffset attributes " .
241 "in the I2C master unit XML\n";
242 }
243
244 $path = "ahb/apb/i2c\@$entry->{regBaseAddress}/i2c-bus@" .
245 "$entry->{regOffset}";
246 }
247 else {
248 #TODO: FSI support for the OCC when known
249 die "HWMON bus type $entry->{type} not implemented yet\n";
250 }
251
252 return $path;
253}
254
255
256#Returns the name to use for the conf file:
257# <name>@<addr>.conf (e.g. rtc@68.conf)
258sub getConfFileName
259{
260 my ($entry) = @_;
261 return "$entry->{name}\@$entry->{addr}.conf";
262}
263
264
Matt Spinler8df7be82017-01-09 15:42:05 -0600265sub printUsage
266{
267 print "$0 -x [XML filename]\n";
268 exit(1);
269}