summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors/0010-Add-support-for-Get-PMBUS-Readings-method.patch
blob: d305ef008f3b750f8a1e3690417eb8eef9d0a2b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
From 5c2fbc31b076a32d0291e74046a576de852ac90c Mon Sep 17 00:00:00 2001
From: Arun Lal K M <arun.lal@intel.com>
Date: Thu, 6 Jan 2022 20:30:09 +0000
Subject: [PATCH] Add support for 'Get PMBUS Readings' method.

VR sensor is currently read in the following way:
BMC gives read command, and ME proxy forward it to VR sensor.

In 'Get PMBUS Readings' method BMC reads the data from ME.
This is used in platforms where ME cannot proxy forward PMBUS command.

Command:
0x2E 0xF5 0x57 0x01 0x00 0x<ID> 0x0F 0x00

0x2E is net function
0xF5 is corresponding to 'Get PMBUS Readings'
0x57 0x01 0x00 is Intel manufacturers ID
ID is the ID of senssor in ME (not actual sensor address)
0x0F is PMBUS-enabled Device Index
0x00 is reserved byte

New configuration parameters in Baseboard.json:
1) DeviceIndex: ID of the sensor in ME.
2) ReadMethod: use "ReadME" for 'Get PMBUS Readings'.
3) Register: Register to read in the response byte array.
   For example,
      Registers 1 = Temperature
      Registers 2 = Voltage
      Registers 3 = Current

Note:
This is not a complete replacement for the old method, we are adding
one more way to read these sensors. The old implementation is still
present, as in, new code with old configuration file
(xx_Baseboard.json) will work (using the old method of reading the
sensor).

References:
1) Intelligent Power Node Manager External Interface Specification
   Using IPMI.
2) PMBus-Specification.

Tested:
Case 1: Using proxy (Backward compatibility testing)
Sample configuration in Baseboard.json:
{
    "Address": "0xXX",
    "Class": "MpsBridgeTemp",
    "Name": "XX VR Temp",
    "Thresholds": [...],
    "Type": "IpmbSensor"
}

Give command 'ipmitool sdr elist'
Response:
CPU1 PVCCD VR Te | 31h | ok  |  0.1 | 37 degrees C
CPU2 PVCCD VR Te | 36h | ok  |  0.1 | 37 degrees C
CPU1 PVCCFA EHV  | 32h | ok  |  0.1 | 34 degrees C
CPU2 PVCCFA EHV  | 37h | ok  |  0.1 | 33 degrees C
CPU1 VCCIN VR Te | 34h | ok  |  0.1 | 48 degrees C
CPU2 VCCIN VR Te | 39h | ok  |  0.1 | 58 degrees C

Case 2: Using 'Get PMBUS Readings' method
Sample configuration in Baseboard.json:
{
    "Address": "0xXX",
    "DeviceIndex": "0xXX",
    "Class": "MpsBridgeTemp",
    "Register": 1,
    "ReadMethod": "ReadME",
    "Name": "XX VR Temp",
    "Thresholds": [...],
    "Type": "IpmbSensor"
}

Give command 'ipmitool sdr elist'
Response:
CPU1 PVCCD VR Te | 31h | ok  |  0.1 | 41 degrees C
CPU2 PVCCD VR Te | 36h | ok  |  0.1 | 43 degrees C
CPU1 PVCCFA EHV  | 32h | ok  |  0.1 | 37 degrees C
CPU2 PVCCFA EHV  | 37h | ok  |  0.1 | 37 degrees C
CPU1 VCCIN VR Te | 34h | ok  |  0.1 | 60 degrees C
CPU2 VCCIN VR Te | 39h | ok  |  0.1 | 56 degrees C

Signed-off-by: Arun Lal K M <arun.lal@intel.com>
---
 include/IpmbSensor.hpp |  51 ++++++++++
 src/IpmbSensor.cpp     | 217 ++++++++++++++++++++++++++++++++++-------
 2 files changed, 233 insertions(+), 35 deletions(-)

diff --git a/include/IpmbSensor.hpp b/include/IpmbSensor.hpp
index 18d10c1..2a89251 100644
--- a/include/IpmbSensor.hpp
+++ b/include/IpmbSensor.hpp
@@ -43,6 +43,51 @@ namespace sensor
 {
 constexpr uint8_t netFn = 0x04;
 constexpr uint8_t getSensorReading = 0x2d;
+constexpr uint8_t manufacturerId[3] = {0x57, 0x01, 0x00};
+
+namespace read_me
+{
+/**
+ * Refernce:
+ * Intelligent Power Node Manager External Interface Specification
+ * getPmbusReadings = Get PMBUS Readings (F5h)
+ *
+ * bytesForTimestamp and bytesForManufacturerId are decoded from
+ * response bytes for Get PMBUS Readings.
+ */
+constexpr uint8_t getPmbusReadings = 0xF5;
+constexpr uint8_t bytesForTimestamp = 4;
+constexpr uint8_t bytesForManufacturerId = 3;
+
+constexpr size_t fixedOffset = bytesForTimestamp + bytesForManufacturerId;
+
+void getRawData(uint8_t registerToRead, const std::vector<uint8_t>& input,
+                std::vector<uint8_t>& result)
+{
+    if (input.size() < 3)
+    {
+        return;
+    }
+
+    /* Every register is two bytes*/
+    size_t offset = fixedOffset + (registerToRead * 2);
+    if (input.size() <= (offset + 1))
+    {
+        return;
+    }
+
+    result.reserve(5);
+
+    // ID
+    result.emplace_back(input[0]);
+    result.emplace_back(input[1]);
+    result.emplace_back(input[2]);
+
+    // Value in registerToRead
+    result.emplace_back(input[offset]);
+    result.emplace_back(input[offset + 1]);
+}
+} // namespace read_me
 
 static bool isValid(const std::vector<uint8_t>& data)
 {
@@ -91,6 +136,7 @@ struct IpmbSensor : public Sensor
     void loadDefaults(void);
     void runInitCmd(void);
     bool processReading(const std::vector<uint8_t>& data, double& resp);
+    void setReadMethod(const SensorBaseConfigMap& sensorBaseConfig);
 
     IpmbType type;
     IpmbSubType subType;
@@ -102,6 +148,9 @@ struct IpmbSensor : public Sensor
     uint8_t deviceAddress;
     uint8_t errorCount;
     uint8_t hostSMbusIndex;
+    uint8_t registerToRead = 0;
+    bool isReadMe = false;
+    uint8_t deviceIndex = 0;
     std::vector<uint8_t> commandData;
     std::optional<uint8_t> initCommand;
     std::vector<uint8_t> initData;
@@ -112,4 +161,6 @@ struct IpmbSensor : public Sensor
   private:
     sdbusplus::asio::object_server& objectServer;
     boost::asio::deadline_timer waitTimer;
+
+    void getMeCommand();
 };
diff --git a/src/IpmbSensor.cpp b/src/IpmbSensor.cpp
index eedf21e..75f74b5 100644
--- a/src/IpmbSensor.cpp
+++ b/src/IpmbSensor.cpp
@@ -150,6 +150,39 @@ void IpmbSensor::runInitCmd()
     }
 }
 
+/**
+ * Refernce:
+ * Intelligent Power Node Manager External Interface Specification
+ */
+void IpmbSensor::getMeCommand()
+{
+    /*
+     * Byte 1, 2, 3 = Manufacturer ID.
+     */
+    commandData.emplace_back(ipmi::sensor::manufacturerId[0]);
+    commandData.emplace_back(ipmi::sensor::manufacturerId[1]);
+    commandData.emplace_back(ipmi::sensor::manufacturerId[2]);
+
+    /*
+     * Byte 4 = Device Index.
+     */
+    commandData.emplace_back(deviceIndex);
+
+    /*
+     * Byte 5 = History index.
+     *   bit 0 to 3 = History index. Supported value: 0Fh to retrieve
+     *      current samples.
+     *   bit 4 to 7 = Page number � used only for devices which support
+     *      pages.
+     */
+    commandData.emplace_back(0x0F);
+
+    /*
+     * Byte 6 = First Register Offset.
+     */
+    commandData.emplace_back(0x00);
+}
+
 void IpmbSensor::loadDefaults()
 {
     if (type == IpmbType::meSensor)
@@ -164,28 +197,44 @@ void IpmbSensor::loadDefaults()
     {
         commandAddress = meAddress;
         netfn = ipmi::me_bridge::netFn;
-        command = ipmi::me_bridge::sendRawPmbus;
-        initCommand = ipmi::me_bridge::sendRawPmbus;
-        // pmbus read temp
-        commandData = {0x57,          0x01, 0x00, 0x16, hostSMbusIndex,
-                       deviceAddress, 0x00, 0x00, 0x00, 0x00,
-                       0x01,          0x02, 0x8d};
-        // goto page 0
-        initData = {0x57,          0x01, 0x00, 0x14, hostSMbusIndex,
-                    deviceAddress, 0x00, 0x00, 0x00, 0x00,
-                    0x02,          0x00, 0x00, 0x00};
         readingFormat = ReadingFormat::linearElevenBit;
+        if (isReadMe)
+        {
+            command = ipmi::sensor::read_me::getPmbusReadings;
+            getMeCommand();
+        }
+        else
+        {
+            command = ipmi::me_bridge::sendRawPmbus;
+            initCommand = ipmi::me_bridge::sendRawPmbus;
+            // pmbus read temp
+            commandData = {0x57,          0x01, 0x00, 0x16, hostSMbusIndex,
+                           deviceAddress, 0x00, 0x00, 0x00, 0x00,
+                           0x01,          0x02, 0x8d};
+            // goto page 0
+            initData = {0x57,          0x01, 0x00, 0x14, hostSMbusIndex,
+                        deviceAddress, 0x00, 0x00, 0x00, 0x00,
+                        0x02,          0x00, 0x00, 0x00};
+        }
     }
     else if (type == IpmbType::IR38363VR)
     {
         commandAddress = meAddress;
         netfn = ipmi::me_bridge::netFn;
-        command = ipmi::me_bridge::sendRawPmbus;
-        // pmbus read temp
-        commandData = {0x57,          0x01, 0x00, 0x16, hostSMbusIndex,
-                       deviceAddress, 00,   0x00, 0x00, 0x00,
-                       0x01,          0x02, 0x8D};
         readingFormat = ReadingFormat::elevenBitShift;
+        if (isReadMe)
+        {
+            command = ipmi::sensor::read_me::getPmbusReadings;
+            getMeCommand();
+        }
+        else
+        {
+            command = ipmi::me_bridge::sendRawPmbus;
+            // pmbus read temp
+            commandData = {0x57,          0x01, 0x00, 0x16, hostSMbusIndex,
+                           deviceAddress, 00,   0x00, 0x00, 0x00,
+                           0x01,          0x02, 0x8D};
+        }
     }
     else if (type == IpmbType::ADM1278HSC)
     {
@@ -194,20 +243,28 @@ void IpmbSensor::loadDefaults()
         {
             case IpmbSubType::temp:
             case IpmbSubType::curr:
-                uint8_t snsNum;
-                if (subType == IpmbSubType::temp)
+                netfn = ipmi::me_bridge::netFn;
+                readingFormat = ReadingFormat::elevenBit;
+                if (isReadMe)
                 {
-                    snsNum = 0x8d;
+                    command = ipmi::sensor::read_me::getPmbusReadings;
+                    getMeCommand();
                 }
                 else
                 {
-                    snsNum = 0x8c;
+                    uint8_t snsNum;
+                    if (subType == IpmbSubType::temp)
+                    {
+                        snsNum = 0x8d;
+                    }
+                    else
+                    {
+                        snsNum = 0x8c;
+                    }
+                    command = ipmi::me_bridge::sendRawPmbus;
+                    commandData = {0x57, 0x01, 0x00, 0x86, deviceAddress,
+                                   0x00, 0x00, 0x01, 0x02, snsNum};
                 }
-                netfn = ipmi::me_bridge::netFn;
-                command = ipmi::me_bridge::sendRawPmbus;
-                commandData = {0x57, 0x01, 0x00, 0x86, deviceAddress,
-                               0x00, 0x00, 0x01, 0x02, snsNum};
-                readingFormat = ReadingFormat::elevenBit;
                 break;
             case IpmbSubType::power:
             case IpmbSubType::volt:
@@ -224,17 +281,25 @@ void IpmbSensor::loadDefaults()
     {
         commandAddress = meAddress;
         netfn = ipmi::me_bridge::netFn;
-        command = ipmi::me_bridge::sendRawPmbus;
-        initCommand = ipmi::me_bridge::sendRawPmbus;
-        // pmbus read temp
-        commandData = {0x57,          0x01, 0x00, 0x16, hostSMbusIndex,
-                       deviceAddress, 0x00, 0x00, 0x00, 0x00,
-                       0x01,          0x02, 0x8d};
-        // goto page 0
-        initData = {0x57,          0x01, 0x00, 0x14, hostSMbusIndex,
-                    deviceAddress, 0x00, 0x00, 0x00, 0x00,
-                    0x02,          0x00, 0x00, 0x00};
         readingFormat = ReadingFormat::byte3;
+        if (isReadMe)
+        {
+            command = ipmi::sensor::read_me::getPmbusReadings;
+            getMeCommand();
+        }
+        else
+        {
+            command = ipmi::me_bridge::sendRawPmbus;
+            initCommand = ipmi::me_bridge::sendRawPmbus;
+            // pmbus read temp
+            commandData = {0x57,          0x01, 0x00, 0x16, hostSMbusIndex,
+                           deviceAddress, 0x00, 0x00, 0x00, 0x00,
+                           0x01,          0x02, 0x8d};
+            // goto page 0
+            initData = {0x57,          0x01, 0x00, 0x14, hostSMbusIndex,
+                        deviceAddress, 0x00, 0x00, 0x00, 0x00,
+                        0x02,          0x00, 0x00, 0x00};
+        }
     }
     else
     {
@@ -362,7 +427,19 @@ void IpmbSensor::read(void)
                     read();
                     return;
                 }
-                const std::vector<uint8_t>& data = std::get<5>(response);
+
+                std::vector<uint8_t> data;
+
+                if (isReadMe)
+                {
+                    ipmi::sensor::read_me::getRawData(
+                        registerToRead, std::get<5>(response), data);
+                }
+                else
+                {
+                    data = std::get<5>(response);
+                }
+
                 if constexpr (debug)
                 {
                     std::cout << name << ": ";
@@ -408,6 +485,74 @@ void IpmbSensor::read(void)
             "sendRequest", commandAddress, netfn, lun, command, commandData);
     });
 }
+
+void IpmbSensor::setReadMethod(const SensorBaseConfigMap& sensorBaseConfig)
+{
+    /*
+     * Some sensor can be read in two ways
+     * 1) Using proxy: BMC read command is proxy forward by ME
+     * to sensor. 2) Using 'Get PMBUS Readings': ME responds to
+     * BMC with sensor data.
+     *
+     * By default we assume the method is 1. And if ReadMethod
+     * == "ReadME" we switch to method 2.
+     */
+    auto readMethod = sensorBaseConfig.find("ReadMethod");
+    if (readMethod == sensorBaseConfig.end())
+    {
+        std::cerr << "'ReadMethod' not found, defaulting to "
+                     "proxy method of reading sensor\n";
+        return;
+    }
+
+    if (std::visit(VariantToStringVisitor(), readMethod->second) != "ReadME")
+    {
+        std::cerr << "'ReadMethod' != 'ReadME', defaulting to "
+                     "proxy method of reading sensor\n";
+        return;
+    }
+
+    /*
+     * In 'Get PMBUS Readings' the response containt a
+     * set of registers from the sensor. And different
+     * values such as temperature power voltage will be
+     * mapped to different registers.
+     */
+    auto registerToReadConfig = sensorBaseConfig.find("Register");
+    if (registerToReadConfig == sensorBaseConfig.end())
+    {
+        std::cerr << "'Register' not found, defaulting to "
+                     "proxy method of reading sensor\n";
+        return;
+    }
+
+    registerToRead =
+        std::visit(VariantToUnsignedIntVisitor(), registerToReadConfig->second);
+
+    /*
+     * In 'Get PMBUS Readings' since ME is
+     * responding with the sensor data we need
+     * to use the address for sensor in ME, this
+     * is different from the actual sensor
+     * address.
+     */
+    auto deviceIndexConfig = sensorBaseConfig.find("SensorMeAddress");
+    if (deviceIndexConfig == sensorBaseConfig.end())
+    {
+        std::cerr << "'SensorMeAddress' not found, defaulting to "
+                     "proxy method of reading sensor\n";
+        return;
+    }
+
+    deviceIndex =
+        std::visit(VariantToUnsignedIntVisitor(), deviceIndexConfig->second);
+
+    /*
+     * We found all parameters to use 'Get PMBUS Readings'
+     * method.
+     */
+    isReadMe = true;
+}
 void createSensors(
     boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
     boost::container::flat_map<std::string, std::unique_ptr<IpmbSensor>>&
@@ -485,6 +630,8 @@ void createSensors(
                         std::move(sensorThresholds), deviceAddress,
                         hostSMbusIndex, pollRate, sensorTypeName);
 
+                    sensor->setReadMethod(entry.second);
+
                     /* Initialize scale and offset value */
                     sensor->scaleVal = 1;
                     sensor->offsetVal = 0;
-- 
2.17.1