xref: /openbmc/phosphor-fan-presence/presence/eeprom_device.hpp (revision dfddd648cb81b27492afead4e2346f5fcd1397cb)
1bc4179e9SMatt Spinler #pragma once
2bc4179e9SMatt Spinler 
3bc4179e9SMatt Spinler #include "sdeventplus.hpp"
4bc4179e9SMatt Spinler 
5bc4179e9SMatt Spinler #include <phosphor-logging/lg2.hpp>
6bc4179e9SMatt Spinler #include <sdeventplus/clock.hpp>
7bc4179e9SMatt Spinler #include <sdeventplus/utility/timer.hpp>
8bc4179e9SMatt Spinler 
9bc4179e9SMatt Spinler #include <filesystem>
10bc4179e9SMatt Spinler #include <fstream>
11bc4179e9SMatt Spinler #include <string>
12bc4179e9SMatt Spinler 
13bc4179e9SMatt Spinler namespace phosphor::fan::presence
14bc4179e9SMatt Spinler {
15bc4179e9SMatt Spinler using namespace phosphor::fan::util;
16bc4179e9SMatt Spinler 
17bc4179e9SMatt Spinler /**
18bc4179e9SMatt Spinler  * @class EEPROMDevice
19bc4179e9SMatt Spinler  *
20bc4179e9SMatt Spinler  * Provides an API to bind an EEPROM driver to a device, after waiting
21bc4179e9SMatt Spinler  * a configurable amount of time in case the device needs time
22bc4179e9SMatt Spinler  * to initialize after being plugged into a system.
23bc4179e9SMatt Spinler  */
24bc4179e9SMatt Spinler class EEPROMDevice
25bc4179e9SMatt Spinler {
26bc4179e9SMatt Spinler   public:
27bc4179e9SMatt Spinler     EEPROMDevice() = delete;
28bc4179e9SMatt Spinler     ~EEPROMDevice() = default;
29bc4179e9SMatt Spinler     EEPROMDevice(const EEPROMDevice&) = delete;
30bc4179e9SMatt Spinler     EEPROMDevice& operator=(const EEPROMDevice&) = delete;
31bc4179e9SMatt Spinler     EEPROMDevice(EEPROMDevice&&) = delete;
32bc4179e9SMatt Spinler     EEPROMDevice& operator=(EEPROMDevice&&) = delete;
33bc4179e9SMatt Spinler 
34bc4179e9SMatt Spinler     /**
35bc4179e9SMatt Spinler      * @brief Constructor
36bc4179e9SMatt Spinler      * @param[in] address - The bus-address string as used by drivers
37bc4179e9SMatt Spinler      *                      in sysfs.
38bc4179e9SMatt Spinler      * @param[in] driver - The I2C driver name in sysfs
39bc4179e9SMatt Spinler      * @param[in] bindDelayInMS - The time in milliseconds to wait
40bc4179e9SMatt Spinler      *            before actually doing the bind.
41bc4179e9SMatt Spinler      */
EEPROMDevice(const std::string & address,const std::string & driver,size_t bindDelayInMS)42bc4179e9SMatt Spinler     EEPROMDevice(const std::string& address, const std::string& driver,
43bc4179e9SMatt Spinler                  size_t bindDelayInMS) :
44*dfddd648SPatrick Williams         address(address), path(baseDriverPath / driver),
45*dfddd648SPatrick Williams         bindDelay(bindDelayInMS),
46bc4179e9SMatt Spinler         timer(SDEventPlus::getEvent(),
47bc4179e9SMatt Spinler               std::bind(std::mem_fn(&EEPROMDevice::bindTimerExpired), this))
48bc4179e9SMatt Spinler     {}
49bc4179e9SMatt Spinler 
50bc4179e9SMatt Spinler     /**
51bc4179e9SMatt Spinler      * @brief Kicks off the timer to do the actual bind
52bc4179e9SMatt Spinler      */
bind()53bc4179e9SMatt Spinler     void bind()
54bc4179e9SMatt Spinler     {
55bc4179e9SMatt Spinler         timer.restartOnce(std::chrono::milliseconds{bindDelay});
56bc4179e9SMatt Spinler     }
57bc4179e9SMatt Spinler 
58bc4179e9SMatt Spinler     /**
59bc4179e9SMatt Spinler      * @brief Stops the bind timer if running and unbinds the device
60bc4179e9SMatt Spinler      */
unbind()61bc4179e9SMatt Spinler     void unbind()
62bc4179e9SMatt Spinler     {
63bc4179e9SMatt Spinler         if (timer.isEnabled())
64bc4179e9SMatt Spinler         {
65bc4179e9SMatt Spinler             timer.setEnabled(false);
66bc4179e9SMatt Spinler         }
67bc4179e9SMatt Spinler 
68bc4179e9SMatt Spinler         unbindDevice();
69bc4179e9SMatt Spinler     }
70bc4179e9SMatt Spinler 
71bc4179e9SMatt Spinler   private:
72bc4179e9SMatt Spinler     /**
73bc4179e9SMatt Spinler      * @brief When the bind timer expires it will bind the device.
74bc4179e9SMatt Spinler      */
bindTimerExpired() const75bc4179e9SMatt Spinler     void bindTimerExpired() const
76bc4179e9SMatt Spinler     {
77bc4179e9SMatt Spinler         unbindDevice();
78bc4179e9SMatt Spinler 
79bc4179e9SMatt Spinler         auto bindPath = path / "bind";
80bc4179e9SMatt Spinler         std::ofstream bind{bindPath};
81bc4179e9SMatt Spinler         if (bind.good())
82bc4179e9SMatt Spinler         {
83bc4179e9SMatt Spinler             lg2::info("Binding fan EEPROM device with address {ADDRESS}",
84bc4179e9SMatt Spinler                       "ADDRESS", address);
85bc4179e9SMatt Spinler             bind << address;
86bc4179e9SMatt Spinler         }
87bc4179e9SMatt Spinler 
88bc4179e9SMatt Spinler         if (bind.fail())
89bc4179e9SMatt Spinler         {
90bc4179e9SMatt Spinler             lg2::error("Error while binding fan EEPROM device with path {PATH}"
91bc4179e9SMatt Spinler                        " and address {ADDR}",
92bc4179e9SMatt Spinler                        "PATH", bindPath, "ADDR", address);
93bc4179e9SMatt Spinler         }
94bc4179e9SMatt Spinler     }
95bc4179e9SMatt Spinler 
96bc4179e9SMatt Spinler     /**
97bc4179e9SMatt Spinler      * @brief Unbinds the device.
98bc4179e9SMatt Spinler      */
unbindDevice() const99bc4179e9SMatt Spinler     void unbindDevice() const
100bc4179e9SMatt Spinler     {
101bc4179e9SMatt Spinler         auto devicePath = path / address;
102bc4179e9SMatt Spinler         if (!std::filesystem::exists(devicePath))
103bc4179e9SMatt Spinler         {
104bc4179e9SMatt Spinler             return;
105bc4179e9SMatt Spinler         }
106bc4179e9SMatt Spinler 
107bc4179e9SMatt Spinler         auto unbindPath = path / "unbind";
108bc4179e9SMatt Spinler         std::ofstream unbind{unbindPath};
109bc4179e9SMatt Spinler         if (unbind.good())
110bc4179e9SMatt Spinler         {
111bc4179e9SMatt Spinler             unbind << address;
112bc4179e9SMatt Spinler         }
113bc4179e9SMatt Spinler 
114bc4179e9SMatt Spinler         if (unbind.fail())
115bc4179e9SMatt Spinler         {
116bc4179e9SMatt Spinler             lg2::error("Error while unbinding fan EEPROM device with path"
117bc4179e9SMatt Spinler                        " {PATH} and address {ADDR}",
118bc4179e9SMatt Spinler                        "PATH", unbindPath, "ADDR", address);
119bc4179e9SMatt Spinler         }
120bc4179e9SMatt Spinler     }
121bc4179e9SMatt Spinler 
122bc4179e9SMatt Spinler     /** @brief The base I2C drivers directory in sysfs */
123bc4179e9SMatt Spinler     const std::filesystem::path baseDriverPath{"/sys/bus/i2c/drivers"};
124bc4179e9SMatt Spinler 
125bc4179e9SMatt Spinler     /**
126bc4179e9SMatt Spinler      * @brief The address string with the i2c bus and address.
127bc4179e9SMatt Spinler      * Example: '32-0050'
128bc4179e9SMatt Spinler      */
129bc4179e9SMatt Spinler     const std::string address;
130bc4179e9SMatt Spinler 
131bc4179e9SMatt Spinler     /** @brief The path to the driver dir, like /sys/bus/i2c/drivers/at24 */
132bc4179e9SMatt Spinler     const std::filesystem::path path;
133bc4179e9SMatt Spinler 
134bc4179e9SMatt Spinler     /** @brief Number of milliseconds to delay to actually do the bind. */
135bc4179e9SMatt Spinler     const size_t bindDelay{};
136bc4179e9SMatt Spinler 
137bc4179e9SMatt Spinler     /** @brief The timer to do the delay with */
138bc4179e9SMatt Spinler     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> timer;
139bc4179e9SMatt Spinler };
140bc4179e9SMatt Spinler 
141bc4179e9SMatt Spinler } // namespace phosphor::fan::presence
142