1 #pragma once
2 
3 #include "pmbus.hpp"
4 #include "types.hpp"
5 #include "util.hpp"
6 #include "utility.hpp"
7 
8 #include <gpiod.hpp>
9 #include <sdbusplus/bus/match.hpp>
10 
11 #include <filesystem>
12 #include <stdexcept>
13 
14 namespace phosphor::power::psu
15 {
16 
17 #if IBM_VPD
18 // PMBus device driver "file name" to read for CCIN value.
19 constexpr auto CCIN = "ccin";
20 constexpr auto PART_NUMBER = "part_number";
21 constexpr auto FRU_NUMBER = "fru";
22 constexpr auto SERIAL_HEADER = "header";
23 constexpr auto SERIAL_NUMBER = "serial_number";
24 constexpr auto FW_VERSION = "fw_version";
25 
26 // The D-Bus property name to update with the CCIN value.
27 constexpr auto MODEL_PROP = "Model";
28 constexpr auto PN_PROP = "PartNumber";
29 constexpr auto SPARE_PN_PROP = "SparePartNumber";
30 constexpr auto SN_PROP = "SerialNumber";
31 constexpr auto VERSION_PROP = "Version";
32 
33 // ipzVPD Keyword sizes
34 static constexpr auto FL_KW_SIZE = 20;
35 #endif
36 
37 constexpr auto LOG_LIMIT = 3;
38 constexpr auto DEGLITCH_LIMIT = 3;
39 
40 /**
41  * @class PowerSupply
42  * Represents a PMBus power supply device.
43  */
44 class PowerSupply
45 {
46   public:
47     PowerSupply() = delete;
48     PowerSupply(const PowerSupply&) = delete;
49     PowerSupply(PowerSupply&&) = delete;
50     PowerSupply& operator=(const PowerSupply&) = delete;
51     PowerSupply& operator=(PowerSupply&&) = delete;
52     ~PowerSupply() = default;
53 
54     /**
55      * @param[in] invpath - String for inventory path to use
56      * @param[in] i2cbus - The bus number this power supply is on
57      * @param[in] i2caddr - The 16-bit I2C address of the power supply
58      * @param[in] gpioLineName - The gpio-line-name to read for presence. See
59      * https://github.com/openbmc/docs/blob/master/designs/device-tree-gpio-naming.md
60      */
61     PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath,
62                 std::uint8_t i2cbus, const std::uint16_t i2caddr,
63                 const std::string& gpioLineName);
64 
65     phosphor::pmbus::PMBusBase& getPMBus()
66     {
67         return *pmbusIntf;
68     }
69 
70     GPIOInterfaceBase* getPresenceGPIO()
71     {
72         return presenceGPIO.get();
73     }
74 
75     std::string getPresenceGPIOName() const
76     {
77         if (presenceGPIO != nullptr)
78         {
79             return presenceGPIO->getName();
80         }
81         else
82         {
83             return std::string();
84         }
85     }
86 
87     /**
88      * Power supply specific function to analyze for faults/errors.
89      *
90      * Various PMBus status bits will be checked for fault conditions.
91      * If a certain fault bits are on, the appropriate error will be
92      * committed.
93      */
94     void analyze();
95 
96     /**
97      * Write PMBus ON_OFF_CONFIG
98      *
99      * This function will be called to cause the PMBus device driver to send the
100      * ON_OFF_CONFIG command. Takes one byte of data.
101      *
102      * @param[in] data - The ON_OFF_CONFIG data byte mask.
103      */
104     void onOffConfig(uint8_t data);
105 
106     /**
107      * Clears all the member variables that indicate if a fault bit was seen as
108      * on in the STATUS_WORD or STATUS_MFR_SPECIFIC response.
109      */
110     void clearFaultFlags()
111     {
112         inputFault = 0;
113         mfrFault = 0;
114         statusMFR = 0;
115         vinUVFault = 0;
116         cmlFault = 0;
117         voutOVFault = 0;
118         ioutOCFault = 0;
119         voutUVFault = 0;
120         fanFault = 0;
121         tempFault = 0;
122         pgoodFault = 0;
123         psKillFault = 0;
124         ps12VcsFault = 0;
125         psCS12VFault = 0;
126     }
127 
128     /**
129      * Write PMBus CLEAR_FAULTS
130      *
131      * This function will be called in various situations in order to clear
132      * any fault status bits that may have been set, in order to start over
133      * with a clean state. Presence changes and power state changes will
134      * want to clear any faults logged.
135      */
136     void clearFaults();
137 
138     /**
139      * @brief Adds properties to the inventory.
140      *
141      * Reads the values from the device and writes them to the
142      * associated power supply D-Bus inventory object.
143      *
144      * This needs to be done on startup, and each time the presence
145      * state changes.
146      *
147      * Properties added:
148      * - Serial Number
149      * - Part Number
150      * - CCIN (Customer Card Identification Number) - added as the Model
151      * - Firmware version
152      */
153     void updateInventory();
154 
155     /**
156      * @brief Accessor function to indicate present status
157      */
158     bool isPresent() const
159     {
160         return present;
161     }
162 
163     /**
164      * @brief Returns the last read value from STATUS_WORD.
165      */
166     uint64_t getStatusWord() const
167     {
168         return statusWord;
169     }
170 
171     /**
172      * @brief Returns the last read value from STATUS_INPUT.
173      */
174     uint64_t getStatusInput() const
175     {
176         return statusInput;
177     }
178 
179     /**
180      * @brief Returns the last read value from STATUS_MFR.
181      */
182     uint64_t getMFRFault() const
183     {
184         return statusMFR;
185     }
186 
187     /**
188      * @brief Returns the last read value from STATUS_CML.
189      */
190     uint64_t getStatusCML() const
191     {
192         return statusCML;
193     }
194 
195     /**
196      * @brief Returns the last read value from STATUS_VOUT.
197      */
198     uint64_t getStatusVout() const
199     {
200         return statusVout;
201     }
202 
203     /**
204      * @brief Returns the last value read from STATUS_IOUT.
205      */
206     uint64_t getStatusIout() const
207     {
208         return statusIout;
209     }
210 
211     /**
212      * @brief Returns the last value read from STATUS_FANS_1_2.
213      */
214     uint64_t getStatusFans12() const
215     {
216         return statusFans12;
217     }
218 
219     /**
220      * @brief Returns the last value read from STATUS_TEMPERATURE.
221      */
222     uint64_t getStatusTemperature() const
223     {
224         return statusTemperature;
225     }
226 
227     /**
228      * @brief Returns true if a fault was found.
229      */
230     bool isFaulted() const
231     {
232         return (hasCommFault() || (vinUVFault >= DEGLITCH_LIMIT) ||
233                 (inputFault >= DEGLITCH_LIMIT) ||
234                 (voutOVFault >= DEGLITCH_LIMIT) ||
235                 (ioutOCFault >= DEGLITCH_LIMIT) ||
236                 (voutUVFault >= DEGLITCH_LIMIT) ||
237                 (fanFault >= DEGLITCH_LIMIT) || (tempFault >= DEGLITCH_LIMIT) ||
238                 (pgoodFault >= DEGLITCH_LIMIT) || (mfrFault >= DEGLITCH_LIMIT));
239     }
240 
241     /**
242      * @brief Return whether a fault has been logged for this power supply
243      */
244     bool isFaultLogged() const
245     {
246         return faultLogged;
247     }
248 
249     /**
250      * @brief Called when a fault for this power supply has been logged.
251      */
252     void setFaultLogged()
253     {
254         faultLogged = true;
255     }
256 
257     /**
258      * @brief Returns true if INPUT fault occurred.
259      */
260     bool hasInputFault() const
261     {
262         return (inputFault >= DEGLITCH_LIMIT);
263     }
264 
265     /**
266      * @brief Returns true if MFRSPECIFIC occurred.
267      */
268     bool hasMFRFault() const
269     {
270         return (mfrFault >= DEGLITCH_LIMIT);
271     }
272 
273     /**
274      * @brief Returns true if VIN_UV_FAULT occurred.
275      */
276     bool hasVINUVFault() const
277     {
278         return (vinUVFault >= DEGLITCH_LIMIT);
279     }
280 
281     /**
282      * @brief Returns true if VOUT_OV_FAULT occurred.
283      */
284     bool hasVoutOVFault() const
285     {
286         return (voutOVFault >= DEGLITCH_LIMIT);
287     }
288 
289     /**
290      * @brief Returns true if IOUT_OC fault occurred (bit 4 STATUS_BYTE).
291      */
292     bool hasIoutOCFault() const
293     {
294         return (ioutOCFault >= DEGLITCH_LIMIT);
295     }
296 
297     /**
298      * @brief Returns true if VOUT_UV_FAULT occurred.
299      */
300     bool hasVoutUVFault() const
301     {
302         return (voutUVFault >= DEGLITCH_LIMIT);
303     }
304 
305     /**
306      *@brief Returns true if fan fault occurred.
307      */
308     bool hasFanFault() const
309     {
310         return (fanFault >= DEGLITCH_LIMIT);
311     }
312 
313     /**
314      * @brief Returns true if TEMPERATURE fault occurred.
315      */
316     bool hasTempFault() const
317     {
318         return (tempFault >= DEGLITCH_LIMIT);
319     }
320 
321     /**
322      * @brief Returns true if there is a PGood fault (PGOOD# inactive, or OFF
323      * bit on).
324      */
325     bool hasPgoodFault() const
326     {
327         return (pgoodFault >= DEGLITCH_LIMIT);
328     }
329 
330     /**
331      * @brief Return true if there is a PS_Kill fault.
332      */
333     bool hasPSKillFault() const
334     {
335         return (psKillFault >= DEGLITCH_LIMIT);
336     }
337 
338     /**
339      * @brief Returns true if there is a 12Vcs (standy power) fault.
340      */
341     bool hasPS12VcsFault() const
342     {
343         return (ps12VcsFault >= DEGLITCH_LIMIT);
344     }
345 
346     /**
347      * @brief Returns true if there is a 12V current-share fault.
348      */
349     bool hasPSCS12VFault() const
350     {
351         return (psCS12VFault >= DEGLITCH_LIMIT);
352     }
353 
354     /**
355      * @brief Returns the device path
356      *
357      * This can be used for error call outs.
358      * Example: /sys/bus/i2c/devices/3-0068
359      */
360     const std::string getDevicePath() const
361     {
362         return pmbusIntf->path();
363     }
364 
365     /**
366      * @brief Returns this power supply's inventory path.
367      *
368      * This can be used for error call outs.
369      * Example:
370      * /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply1
371      */
372     const std::string& getInventoryPath() const
373     {
374         return inventoryPath;
375     }
376 
377     /**
378      * @brief Returns the short name (last part of inventoryPath).
379      */
380     const std::string& getShortName() const
381     {
382         return shortName;
383     }
384 
385     /**
386      * @brief Returns the firmware revision version read from the power supply
387      */
388     const std::string& getFWVersion() const
389     {
390         return fwVersion;
391     }
392 
393     /**
394      * @brief Returns the model name of the power supply
395      */
396     const std::string& getModelName() const
397     {
398         return modelName;
399     }
400 
401     /** @brief Returns true if the number of failed reads exceeds limit
402      * TODO: or CML bit on.
403      */
404     bool hasCommFault() const
405     {
406         return ((readFail >= LOG_LIMIT) || (cmlFault >= DEGLITCH_LIMIT));
407     }
408 
409     /**
410      * @brief Reads the pmbus input voltage and returns that actual voltage
411      *        reading and the calculated input voltage based on thresholds.
412      * @param[out] actualInputVoltage - The actual voltage reading, in Volts.
413      * @param[out] inputVoltage - A rounded up/down value of the actual input
414      *             voltage based on thresholds, in Volts.
415      */
416     void getInputVoltage(double& actualInputVoltage, int& inputVoltage) const;
417 
418     /**
419      * @brief Check if the PS is considered to be available or not
420      *
421      * It is unavailable if any of:
422      * - not present
423      * - input fault active
424      * - Vin UV fault active
425      * - PS KILL fault active
426      * - Iout OC fault active
427      *
428      * Other faults will, through creating error logs with callouts, already
429      * be setting the Functional property to false.
430      *
431      * On changes, the Available property is updated in the inventory.
432      */
433     void checkAvailability();
434 
435   private:
436     /** @brief systemd bus member */
437     sdbusplus::bus::bus& bus;
438 
439     /** @brief Will be updated to the latest/lastvalue read from STATUS_WORD.*/
440     uint64_t statusWord = 0;
441 
442     /** @brief Will be set to the last read value of STATUS_WORD. */
443     uint64_t statusWordOld = 0;
444 
445     /** @brief Will be updated to the latest/lastvalue read from STATUS_INPUT.*/
446     uint64_t statusInput = 0;
447 
448     /** @brief Will be updated to the latest/lastvalue read from STATUS_MFR.*/
449     uint64_t statusMFR = 0;
450 
451     /** @brief Will be updated to the latest/last value read from STATUS_CML.*/
452     uint64_t statusCML = 0;
453 
454     /** @brief Will be updated to the latest/last value read from STATUS_VOUT.*/
455     uint64_t statusVout = 0;
456 
457     /** @brief Will be updated to the latest/last value read from STATUS_IOUT.*/
458     uint64_t statusIout = 0;
459 
460     /** @brief Will be updated to the latest/last value read from
461      * STATUS_FANS_1_2. */
462     uint64_t statusFans12 = 0;
463 
464     /** @brief Will be updated to the latest/last value read from
465      * STATUS_TEMPERATURE.*/
466     uint64_t statusTemperature = 0;
467 
468     /** @brief Will be updated with latest converted value read from READ_VIN */
469     int inputVoltage = phosphor::pmbus::in_input::VIN_VOLTAGE_0;
470 
471     /** @brief Will be updated with the actual voltage last read from READ_VIN
472      */
473     double actualInputVoltage = 0;
474 
475     /** @brief True if an error for a fault has already been logged. */
476     bool faultLogged = false;
477 
478     /** @brief Incremented if bit 1 of STATUS_WORD low byte is on.
479      *
480      * Considered faulted if reaches DEGLITCH_LIMIT.
481      */
482     size_t cmlFault = 0;
483 
484     /** @brief Incremented if bit 5 of STATUS_WORD high byte is on.
485      *
486      * Considered faulted if reaches DEGLITCH_LIMIT.
487      */
488     size_t inputFault = 0;
489 
490     /** @brief Incremented if bit 4 of STATUS_WORD high byte is on.
491      *
492      * Considered faulted if reaches DEGLITCH_LIMIT.
493      */
494     size_t mfrFault = 0;
495 
496     /** @brief Incremented if bit 3 of STATUS_WORD low byte is on.
497      *
498      * Considered faulted if reaches DEGLITCH_LIMIT.
499      */
500     size_t vinUVFault = 0;
501 
502     /** @brief Incremented if bit 5 of STATUS_WORD low byte is on.
503      *
504      * Considered faulted if reaches DEGLITCH_LIMIT.
505      */
506     size_t voutOVFault = 0;
507 
508     /** @brief Incremented if bit 4 of STATUS_WORD low byte is on.
509      *
510      * Considered faulted if reaches DEGLITCH_LIMIT.
511      */
512     size_t ioutOCFault = 0;
513 
514     /** @brief Incremented if bit 7 of STATUS_WORD high byte is on and bit 5
515      * (VOUT_OV) of low byte is off.
516      *
517      * Considered faulted if reaches DEGLITCH_LIMIT.
518      */
519     size_t voutUVFault = 0;
520 
521     /** @brief Incremented if FANS fault/warn bit on in STATUS_WORD.
522      *
523      * Considered faulted if reaches DEGLITCH_LIMIT.
524      */
525     size_t fanFault = 0;
526 
527     /** @brief Incremented if bit 2 of STATUS_WORD low byte is on.
528      *
529      * Considered faulted if reaches DEGLITCH_LIMIT. */
530     size_t tempFault = 0;
531 
532     /**
533      * @brief Incremented if bit 11 or 6 of STATUS_WORD is on. PGOOD# is
534      * inactive, or the unit is off.
535      *
536      * Considered faulted if reaches DEGLITCH_LIMIT.
537      */
538     size_t pgoodFault = 0;
539 
540     /**
541      * @brief Power Supply Kill fault.
542      *
543      * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use
544      * bit 4 to indicate this fault. Considered faulted if it reaches
545      * DEGLITCH_LIMIT.
546      */
547     size_t psKillFault = 0;
548 
549     /**
550      * @brief Power Supply 12Vcs fault (standby power).
551      *
552      * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use
553      * bit 6 to indicate this fault. Considered faulted if it reaches
554      * DEGLITCH_LIMIT.
555      */
556     size_t ps12VcsFault = 0;
557 
558     /**
559      * @brief Power Supply Current-Share fault in 12V domain.
560      *
561      * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use
562      * bit 7 to indicate this fault. Considered faulted if it reaches
563      * DEGLITCH_LIMIT.
564      */
565     size_t psCS12VFault = 0;
566 
567     /** @brief Count of the number of read failures. */
568     size_t readFail = 0;
569 
570     /**
571      * @brief Examine STATUS_WORD for CML (communication, memory, logic fault).
572      */
573     void analyzeCMLFault();
574 
575     /**
576      * @brief Examine STATUS_WORD for INPUT bit on.
577      *
578      * "An input voltage, input current, or input power fault or warning has
579      * occurred."
580      */
581     void analyzeInputFault();
582 
583     /**
584      * @brief Examine STATUS_WORD for VOUT being set.
585      *
586      * If VOUT is on, "An output voltage fault or warning has occurred.", and
587      * VOUT_OV_FAULT is on, there is an output over-voltage fault.
588      */
589     void analyzeVoutOVFault();
590 
591     /*
592      * @brief Examine STATUS_WORD value read for IOUT_OC_FAULT.
593      *
594      * "An output overcurrent fault has occurred." If it is on, and fault not
595      * set, trace STATUS_WORD, STATUS_MFR_SPECIFIC, and STATUS_IOUT values.
596      */
597     void analyzeIoutOCFault();
598 
599     /**
600      * @brief Examines STATUS_WORD value read to see if there is a UV fault.
601      *
602      * Checks if the VOUT bit is on, indicating "An output voltage fault or
603      * warning has occurred", if it is on, but VOUT_OV_FAULT is off, it is
604      * determined to be an indication of an output under-voltage fault.
605      */
606     void analyzeVoutUVFault();
607 
608     /**
609      * @brief Examine STATUS_WORD for the fan fault/warning bit.
610      *
611      * If fanFault is not on, trace that the bit now came on, include
612      * STATUS_WORD, STATUS_MFR_SPECIFIC, and STATUS_FANS_1_2 values as well, to
613      * help with understanding what may have caused it to be set.
614      */
615     void analyzeFanFault();
616 
617     /**
618      * @brief Examine STATUS_WORD for temperature fault.
619      */
620     void analyzeTemperatureFault();
621 
622     /**
623      * @brief Examine STATUS_WORD for pgood or unit off faults.
624      */
625     void analyzePgoodFault();
626 
627     /**
628      * @brief Determine possible manufacturer-specific faults from bits in
629      * STATUS_MFR.
630      *
631      * The bits in the STATUS_MFR_SPECIFIC command response have "Manufacturer
632      * Defined" meanings. Determine which faults, if any, are present based on
633      * the power supply (device driver) type.
634      */
635     void determineMFRFault();
636 
637     /**
638      * @brief Examine STATUS_WORD value read for MFRSPECIFIC bit on.
639      *
640      * "A manufacturer specific fault or warning has occurred."
641      *
642      * If it is on, call the determineMFRFault() helper function to examine the
643      * value read from STATUS_MFR_SPECIFIC.
644      */
645     void analyzeMFRFault();
646 
647     /**
648      * @brief Analyzes the STATUS_WORD for a VIN_UV_FAULT indicator.
649      */
650     void analyzeVinUVFault();
651 
652     /**
653      * @brief D-Bus path to use for this power supply's inventory status.
654      **/
655     std::string inventoryPath;
656 
657     /**
658      * @brief Store the short name to avoid string processing.
659      *
660      * The short name will be something like powersupply1, the last part of the
661      * inventoryPath.
662      */
663     std::string shortName;
664 
665     /**
666      * @brief Given a full inventory path, returns the last node of the path as
667      * the "short name"
668      */
669     std::string findShortName(const std::string& invPath)
670     {
671         auto const lastSlashPos = invPath.find_last_of('/');
672 
673         if ((lastSlashPos == std::string::npos) ||
674             ((lastSlashPos + 1) == invPath.size()))
675         {
676             return invPath;
677         }
678         else
679         {
680             return invPath.substr(lastSlashPos + 1);
681         }
682     }
683 
684     /**
685      * @brief The libgpiod object for monitoring PSU presence
686      */
687     std::unique_ptr<GPIOInterfaceBase> presenceGPIO = nullptr;
688 
689     /** @brief True if the power supply is present. */
690     bool present = false;
691 
692     /** @brief Power supply model name. */
693     std::string modelName;
694 
695     /** @brief D-Bus match variable used to subscribe to Present property
696      * changes.
697      **/
698     std::unique_ptr<sdbusplus::bus::match_t> presentMatch;
699 
700     /** @brief D-Bus match variable used to subscribe for Present property
701      * interface added.
702      */
703     std::unique_ptr<sdbusplus::bus::match_t> presentAddedMatch;
704 
705     /**
706      * @brief Pointer to the PMBus interface
707      *
708      * Used to read or write to/from PMBus power supply devices.
709      */
710     std::unique_ptr<phosphor::pmbus::PMBusBase> pmbusIntf = nullptr;
711 
712     /** @brief Stored copy of the firmware version/revision string */
713     std::string fwVersion;
714 
715     /**
716      * @brief The file system path used for binding the device driver.
717      */
718     const std::filesystem::path bindPath;
719 
720     /* @brief The string to pass in for binding the device driver. */
721     std::string bindDevice;
722 
723     /**
724      * @brief The result of the most recent availability check
725      *
726      * Saved on the object so changes can be detected.
727      */
728     bool available = false;
729 
730     /**
731      * @brief Binds or unbinds the power supply device driver
732      *
733      * Called when a presence change is detected to either bind the device
734      * driver for the power supply when it is installed, or unbind the device
735      * driver when the power supply is removed.
736      *
737      * Writes <device> to <path>/bind (or unbind)
738      *
739      * @param present - when true, will bind the device driver
740      *                  when false, will unbind the device driver
741      */
742     void bindOrUnbindDriver(bool present);
743 
744     /**
745      *  @brief Updates the presence status by querying D-Bus
746      *
747      * The D-Bus inventory properties for this power supply will be read to
748      * determine if the power supply is present or not and update this
749      * object's present member variable to reflect current status.
750      **/
751     void updatePresence();
752 
753     /**
754      * @brief Updates the power supply presence by reading the GPIO line.
755      */
756     void updatePresenceGPIO();
757 
758     /**
759      * @brief Callback for inventory property changes
760      *
761      * Process change of Present property for power supply.
762      *
763      * This is used if we are watching the D-Bus properties instead of reading
764      * the GPIO presence line ourselves.
765      *
766      * @param[in]  msg - Data associated with Present change signal
767      **/
768     void inventoryChanged(sdbusplus::message::message& msg);
769 
770     /**
771      * @brief Callback for inventory property added.
772      *
773      * Process add of the interface with the Present property for power supply.
774      *
775      * This is used if we are watching the D-Bus properties instead of reading
776      * the GPIO presence line ourselves.
777      *
778      * @param[in]  msg - Data associated with Present add signal
779      **/
780     void inventoryAdded(sdbusplus::message::message& msg);
781 };
782 
783 } // namespace phosphor::power::psu
784