xref: /openbmc/phosphor-power/tools/i2c/i2c.cpp (revision 770de580)
1ab1327c3SLei YU #include "i2c.hpp"
2ab1327c3SLei YU 
39af82a5cSLei YU #include <fcntl.h>
492e89eb5SLei YU #include <sys/ioctl.h>
59af82a5cSLei YU #include <sys/stat.h>
69af82a5cSLei YU #include <unistd.h>
79af82a5cSLei YU 
892e89eb5SLei YU #include <cassert>
99af82a5cSLei YU #include <cerrno>
109af82a5cSLei YU 
11d1bc4cecSBrandon Wyman extern "C"
12d1bc4cecSBrandon Wyman {
1392e89eb5SLei YU #include <i2c/smbus.h>
1492e89eb5SLei YU #include <linux/i2c-dev.h>
1592e89eb5SLei YU #include <linux/i2c.h>
1692e89eb5SLei YU }
1792e89eb5SLei YU 
18ab1327c3SLei YU namespace i2c
19ab1327c3SLei YU {
20ab1327c3SLei YU 
getFuncs()2138ed88d6SShawn McCarney unsigned long I2CDevice::getFuncs()
2292e89eb5SLei YU {
2338ed88d6SShawn McCarney     // If functionality has not been cached
2438ed88d6SShawn McCarney     if (cachedFuncs == NO_FUNCS)
2538ed88d6SShawn McCarney     {
2638ed88d6SShawn McCarney         // Get functionality from adapter
27*770de580SShawn McCarney         int ret = 0, retries = 0;
28*770de580SShawn McCarney         do
29*770de580SShawn McCarney         {
30*770de580SShawn McCarney             ret = ioctl(fd, I2C_FUNCS, &cachedFuncs);
31*770de580SShawn McCarney         } while ((ret < 0) && (++retries <= maxRetries));
32*770de580SShawn McCarney 
33*770de580SShawn McCarney         if (ret < 0)
3492e89eb5SLei YU         {
3592e89eb5SLei YU             throw I2CException("Failed to get funcs", busStr, devAddr, errno);
3692e89eb5SLei YU         }
3738ed88d6SShawn McCarney     }
3892e89eb5SLei YU 
3938ed88d6SShawn McCarney     return cachedFuncs;
4038ed88d6SShawn McCarney }
4138ed88d6SShawn McCarney 
checkReadFuncs(int type)4238ed88d6SShawn McCarney void I2CDevice::checkReadFuncs(int type)
4338ed88d6SShawn McCarney {
4438ed88d6SShawn McCarney     unsigned long funcs = getFuncs();
4592e89eb5SLei YU     switch (type)
4692e89eb5SLei YU     {
4792e89eb5SLei YU         case I2C_SMBUS_BYTE:
4892e89eb5SLei YU             if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
4992e89eb5SLei YU             {
5092e89eb5SLei YU                 throw I2CException("Missing SMBUS_READ_BYTE", busStr, devAddr);
5192e89eb5SLei YU             }
5292e89eb5SLei YU             break;
5392e89eb5SLei YU         case I2C_SMBUS_BYTE_DATA:
5492e89eb5SLei YU             if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
5592e89eb5SLei YU             {
5692e89eb5SLei YU                 throw I2CException("Missing SMBUS_READ_BYTE_DATA", busStr,
5792e89eb5SLei YU                                    devAddr);
5892e89eb5SLei YU             }
5992e89eb5SLei YU             break;
6092e89eb5SLei YU         case I2C_SMBUS_WORD_DATA:
6192e89eb5SLei YU             if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
6292e89eb5SLei YU             {
6392e89eb5SLei YU                 throw I2CException("Missing SMBUS_READ_WORD_DATA", busStr,
6492e89eb5SLei YU                                    devAddr);
6592e89eb5SLei YU             }
6692e89eb5SLei YU             break;
6792e89eb5SLei YU         case I2C_SMBUS_BLOCK_DATA:
6892e89eb5SLei YU             if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
6992e89eb5SLei YU             {
7092e89eb5SLei YU                 throw I2CException("Missing SMBUS_READ_BLOCK_DATA", busStr,
7192e89eb5SLei YU                                    devAddr);
7292e89eb5SLei YU             }
7392e89eb5SLei YU             break;
741d103428SLei YU         case I2C_SMBUS_I2C_BLOCK_DATA:
751d103428SLei YU             if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
761d103428SLei YU             {
771d103428SLei YU                 throw I2CException("Missing I2C_FUNC_SMBUS_READ_I2C_BLOCK",
781d103428SLei YU                                    busStr, devAddr);
791d103428SLei YU             }
801d103428SLei YU             break;
8192e89eb5SLei YU         default:
8292e89eb5SLei YU             fprintf(stderr, "Unexpected read size type: %d\n", type);
8392e89eb5SLei YU             assert(false);
8492e89eb5SLei YU             break;
8592e89eb5SLei YU     }
8692e89eb5SLei YU }
8792e89eb5SLei YU 
checkWriteFuncs(int type)8834fb8bdaSLei YU void I2CDevice::checkWriteFuncs(int type)
8934fb8bdaSLei YU {
9038ed88d6SShawn McCarney     unsigned long funcs = getFuncs();
9134fb8bdaSLei YU     switch (type)
9234fb8bdaSLei YU     {
9334fb8bdaSLei YU         case I2C_SMBUS_BYTE:
9434fb8bdaSLei YU             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
9534fb8bdaSLei YU             {
9634fb8bdaSLei YU                 throw I2CException("Missing SMBUS_WRITE_BYTE", busStr, devAddr);
9734fb8bdaSLei YU             }
9834fb8bdaSLei YU             break;
9934fb8bdaSLei YU         case I2C_SMBUS_BYTE_DATA:
10034fb8bdaSLei YU             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
10134fb8bdaSLei YU             {
10234fb8bdaSLei YU                 throw I2CException("Missing SMBUS_WRITE_BYTE_DATA", busStr,
10334fb8bdaSLei YU                                    devAddr);
10434fb8bdaSLei YU             }
10534fb8bdaSLei YU             break;
10634fb8bdaSLei YU         case I2C_SMBUS_WORD_DATA:
10734fb8bdaSLei YU             if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
10834fb8bdaSLei YU             {
10934fb8bdaSLei YU                 throw I2CException("Missing SMBUS_WRITE_WORD_DATA", busStr,
11034fb8bdaSLei YU                                    devAddr);
11134fb8bdaSLei YU             }
11234fb8bdaSLei YU             break;
11334fb8bdaSLei YU         case I2C_SMBUS_BLOCK_DATA:
11434fb8bdaSLei YU             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
11534fb8bdaSLei YU             {
11634fb8bdaSLei YU                 throw I2CException("Missing SMBUS_WRITE_BLOCK_DATA", busStr,
11734fb8bdaSLei YU                                    devAddr);
11834fb8bdaSLei YU             }
11934fb8bdaSLei YU             break;
1201d103428SLei YU         case I2C_SMBUS_I2C_BLOCK_DATA:
1211d103428SLei YU             if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
1221d103428SLei YU             {
1231d103428SLei YU                 throw I2CException("Missing I2C_FUNC_SMBUS_WRITE_I2C_BLOCK",
1241d103428SLei YU                                    busStr, devAddr);
1251d103428SLei YU             }
1261d103428SLei YU             break;
12734fb8bdaSLei YU         default:
128*770de580SShawn McCarney             fprintf(stderr, "Unexpected write size type: %d\n", type);
12934fb8bdaSLei YU             assert(false);
13034fb8bdaSLei YU     }
13134fb8bdaSLei YU }
13234fb8bdaSLei YU 
open()133d45a9a6dSShawn McCarney void I2CDevice::open()
134d45a9a6dSShawn McCarney {
135d45a9a6dSShawn McCarney     if (isOpen())
136d45a9a6dSShawn McCarney     {
137d45a9a6dSShawn McCarney         throw I2CException("Device already open", busStr, devAddr);
138d45a9a6dSShawn McCarney     }
139d45a9a6dSShawn McCarney 
140*770de580SShawn McCarney     int retries = 0;
141*770de580SShawn McCarney     do
142*770de580SShawn McCarney     {
143d45a9a6dSShawn McCarney         fd = ::open(busStr.c_str(), O_RDWR);
144*770de580SShawn McCarney     } while ((fd == -1) && (++retries <= maxRetries));
145*770de580SShawn McCarney 
146d45a9a6dSShawn McCarney     if (fd == -1)
147d45a9a6dSShawn McCarney     {
148d45a9a6dSShawn McCarney         throw I2CException("Failed to open", busStr, devAddr, errno);
149d45a9a6dSShawn McCarney     }
150d45a9a6dSShawn McCarney 
151*770de580SShawn McCarney     retries = 0;
152*770de580SShawn McCarney     int ret = 0;
153*770de580SShawn McCarney     do
154*770de580SShawn McCarney     {
155*770de580SShawn McCarney         ret = ioctl(fd, I2C_SLAVE, devAddr);
156*770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
157*770de580SShawn McCarney 
158*770de580SShawn McCarney     if (ret < 0)
159d45a9a6dSShawn McCarney     {
160d45a9a6dSShawn McCarney         // Close device since setting slave address failed
161d45a9a6dSShawn McCarney         closeWithoutException();
162d45a9a6dSShawn McCarney 
163d45a9a6dSShawn McCarney         throw I2CException("Failed to set I2C_SLAVE", busStr, devAddr, errno);
164d45a9a6dSShawn McCarney     }
165d45a9a6dSShawn McCarney }
166d45a9a6dSShawn McCarney 
close()167d45a9a6dSShawn McCarney void I2CDevice::close()
168d45a9a6dSShawn McCarney {
169d45a9a6dSShawn McCarney     checkIsOpen();
170*770de580SShawn McCarney 
171*770de580SShawn McCarney     int ret = 0, retries = 0;
172*770de580SShawn McCarney     do
173*770de580SShawn McCarney     {
174*770de580SShawn McCarney         ret = ::close(fd);
175*770de580SShawn McCarney     } while ((ret == -1) && (++retries <= maxRetries));
176*770de580SShawn McCarney 
177*770de580SShawn McCarney     if (ret == -1)
178d45a9a6dSShawn McCarney     {
179d45a9a6dSShawn McCarney         throw I2CException("Failed to close", busStr, devAddr, errno);
180d45a9a6dSShawn McCarney     }
181*770de580SShawn McCarney 
182d45a9a6dSShawn McCarney     fd = INVALID_FD;
18338ed88d6SShawn McCarney     cachedFuncs = NO_FUNCS;
184d45a9a6dSShawn McCarney }
185d45a9a6dSShawn McCarney 
read(uint8_t & data)186ab1327c3SLei YU void I2CDevice::read(uint8_t& data)
187ab1327c3SLei YU {
188d45a9a6dSShawn McCarney     checkIsOpen();
18992e89eb5SLei YU     checkReadFuncs(I2C_SMBUS_BYTE);
19092e89eb5SLei YU 
191*770de580SShawn McCarney     int ret = 0, retries = 0;
192*770de580SShawn McCarney     do
193*770de580SShawn McCarney     {
194*770de580SShawn McCarney         ret = i2c_smbus_read_byte(fd);
195*770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
196*770de580SShawn McCarney 
19792e89eb5SLei YU     if (ret < 0)
19892e89eb5SLei YU     {
19992e89eb5SLei YU         throw I2CException("Failed to read byte", busStr, devAddr, errno);
20092e89eb5SLei YU     }
201*770de580SShawn McCarney 
20292e89eb5SLei YU     data = static_cast<uint8_t>(ret);
203ab1327c3SLei YU }
204ab1327c3SLei YU 
read(uint8_t addr,uint8_t & data)205ab1327c3SLei YU void I2CDevice::read(uint8_t addr, uint8_t& data)
206ab1327c3SLei YU {
207d45a9a6dSShawn McCarney     checkIsOpen();
20892e89eb5SLei YU     checkReadFuncs(I2C_SMBUS_BYTE_DATA);
20992e89eb5SLei YU 
210*770de580SShawn McCarney     int ret = 0, retries = 0;
211*770de580SShawn McCarney     do
212*770de580SShawn McCarney     {
213*770de580SShawn McCarney         ret = i2c_smbus_read_byte_data(fd, addr);
214*770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
215*770de580SShawn McCarney 
21692e89eb5SLei YU     if (ret < 0)
21792e89eb5SLei YU     {
21892e89eb5SLei YU         throw I2CException("Failed to read byte data", busStr, devAddr, errno);
21992e89eb5SLei YU     }
220*770de580SShawn McCarney 
22192e89eb5SLei YU     data = static_cast<uint8_t>(ret);
222ab1327c3SLei YU }
223ab1327c3SLei YU 
read(uint8_t addr,uint16_t & data)224ab1327c3SLei YU void I2CDevice::read(uint8_t addr, uint16_t& data)
225ab1327c3SLei YU {
226d45a9a6dSShawn McCarney     checkIsOpen();
22792e89eb5SLei YU     checkReadFuncs(I2C_SMBUS_WORD_DATA);
228*770de580SShawn McCarney 
229*770de580SShawn McCarney     int ret = 0, retries = 0;
230*770de580SShawn McCarney     do
231*770de580SShawn McCarney     {
232*770de580SShawn McCarney         ret = i2c_smbus_read_word_data(fd, addr);
233*770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
234*770de580SShawn McCarney 
23592e89eb5SLei YU     if (ret < 0)
23692e89eb5SLei YU     {
23792e89eb5SLei YU         throw I2CException("Failed to read word data", busStr, devAddr, errno);
23892e89eb5SLei YU     }
239*770de580SShawn McCarney 
24092e89eb5SLei YU     data = static_cast<uint16_t>(ret);
241ab1327c3SLei YU }
242ab1327c3SLei YU 
read(uint8_t addr,uint8_t & size,uint8_t * data,Mode mode)2431d103428SLei YU void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data, Mode mode)
244ab1327c3SLei YU {
245d45a9a6dSShawn McCarney     checkIsOpen();
246*770de580SShawn McCarney 
247*770de580SShawn McCarney     int ret = -1, retries = 0;
2481d103428SLei YU     switch (mode)
2491d103428SLei YU     {
2501d103428SLei YU         case Mode::SMBUS:
25192e89eb5SLei YU             checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
252*770de580SShawn McCarney             do
253*770de580SShawn McCarney             {
2541d103428SLei YU                 ret = i2c_smbus_read_block_data(fd, addr, data);
255*770de580SShawn McCarney             } while ((ret < 0) && (++retries <= maxRetries));
2561d103428SLei YU             break;
2571d103428SLei YU         case Mode::I2C:
2581d103428SLei YU             checkReadFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
259*770de580SShawn McCarney             do
260*770de580SShawn McCarney             {
2611d103428SLei YU                 ret = i2c_smbus_read_i2c_block_data(fd, addr, size, data);
262*770de580SShawn McCarney             } while ((ret < 0) && (++retries <= maxRetries));
2631d103428SLei YU             if (ret != size)
2641d103428SLei YU             {
2651d103428SLei YU                 throw I2CException("Failed to read i2c block data", busStr,
2661d103428SLei YU                                    devAddr, errno);
2671d103428SLei YU             }
2681d103428SLei YU             break;
2691d103428SLei YU     }
270*770de580SShawn McCarney 
27192e89eb5SLei YU     if (ret < 0)
27292e89eb5SLei YU     {
27392e89eb5SLei YU         throw I2CException("Failed to read block data", busStr, devAddr, errno);
27492e89eb5SLei YU     }
275*770de580SShawn McCarney 
27692e89eb5SLei YU     size = static_cast<uint8_t>(ret);
277ab1327c3SLei YU }
278ab1327c3SLei YU 
write(uint8_t data)279ab1327c3SLei YU void I2CDevice::write(uint8_t data)
280ab1327c3SLei YU {
281d45a9a6dSShawn McCarney     checkIsOpen();
28234fb8bdaSLei YU     checkWriteFuncs(I2C_SMBUS_BYTE);
28334fb8bdaSLei YU 
284*770de580SShawn McCarney     int ret = 0, retries = 0;
285*770de580SShawn McCarney     do
286*770de580SShawn McCarney     {
287*770de580SShawn McCarney         ret = i2c_smbus_write_byte(fd, data);
288*770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
289*770de580SShawn McCarney 
290*770de580SShawn McCarney     if (ret < 0)
29134fb8bdaSLei YU     {
29234fb8bdaSLei YU         throw I2CException("Failed to write byte", busStr, devAddr, errno);
29334fb8bdaSLei YU     }
294ab1327c3SLei YU }
295ab1327c3SLei YU 
write(uint8_t addr,uint8_t data)296ab1327c3SLei YU void I2CDevice::write(uint8_t addr, uint8_t data)
297ab1327c3SLei YU {
298d45a9a6dSShawn McCarney     checkIsOpen();
29934fb8bdaSLei YU     checkWriteFuncs(I2C_SMBUS_BYTE_DATA);
30034fb8bdaSLei YU 
301*770de580SShawn McCarney     int ret = 0, retries = 0;
302*770de580SShawn McCarney     do
303*770de580SShawn McCarney     {
304*770de580SShawn McCarney         ret = i2c_smbus_write_byte_data(fd, addr, data);
305*770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
306*770de580SShawn McCarney 
307*770de580SShawn McCarney     if (ret < 0)
30834fb8bdaSLei YU     {
30934fb8bdaSLei YU         throw I2CException("Failed to write byte data", busStr, devAddr, errno);
31034fb8bdaSLei YU     }
311ab1327c3SLei YU }
312ab1327c3SLei YU 
write(uint8_t addr,uint16_t data)313ab1327c3SLei YU void I2CDevice::write(uint8_t addr, uint16_t data)
314ab1327c3SLei YU {
315d45a9a6dSShawn McCarney     checkIsOpen();
31634fb8bdaSLei YU     checkWriteFuncs(I2C_SMBUS_WORD_DATA);
31734fb8bdaSLei YU 
318*770de580SShawn McCarney     int ret = 0, retries = 0;
319*770de580SShawn McCarney     do
320*770de580SShawn McCarney     {
321*770de580SShawn McCarney         ret = i2c_smbus_write_word_data(fd, addr, data);
322*770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
323*770de580SShawn McCarney 
324*770de580SShawn McCarney     if (ret < 0)
32534fb8bdaSLei YU     {
32634fb8bdaSLei YU         throw I2CException("Failed to write word data", busStr, devAddr, errno);
32734fb8bdaSLei YU     }
328ab1327c3SLei YU }
329ab1327c3SLei YU 
write(uint8_t addr,uint8_t size,const uint8_t * data,Mode mode)3301d103428SLei YU void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data,
3311d103428SLei YU                       Mode mode)
332ab1327c3SLei YU {
333d45a9a6dSShawn McCarney     checkIsOpen();
334*770de580SShawn McCarney 
335*770de580SShawn McCarney     int ret = -1, retries = 0;
3361d103428SLei YU     switch (mode)
3371d103428SLei YU     {
3381d103428SLei YU         case Mode::SMBUS:
33934fb8bdaSLei YU             checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
340*770de580SShawn McCarney             do
341*770de580SShawn McCarney             {
3421d103428SLei YU                 ret = i2c_smbus_write_block_data(fd, addr, size, data);
343*770de580SShawn McCarney             } while ((ret < 0) && (++retries <= maxRetries));
3441d103428SLei YU             break;
3451d103428SLei YU         case Mode::I2C:
3461d103428SLei YU             checkWriteFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
347*770de580SShawn McCarney             do
348*770de580SShawn McCarney             {
3491d103428SLei YU                 ret = i2c_smbus_write_i2c_block_data(fd, addr, size, data);
350*770de580SShawn McCarney             } while ((ret < 0) && (++retries <= maxRetries));
3511d103428SLei YU             break;
3521d103428SLei YU     }
353*770de580SShawn McCarney 
3541d103428SLei YU     if (ret < 0)
35534fb8bdaSLei YU     {
35634fb8bdaSLei YU         throw I2CException("Failed to write block data", busStr, devAddr,
35734fb8bdaSLei YU                            errno);
35834fb8bdaSLei YU     }
359ab1327c3SLei YU }
360ab1327c3SLei YU 
create(uint8_t busId,uint8_t devAddr,InitialState initialState,int maxRetries)361d45a9a6dSShawn McCarney std::unique_ptr<I2CInterface> I2CDevice::create(uint8_t busId, uint8_t devAddr,
362*770de580SShawn McCarney                                                 InitialState initialState,
363*770de580SShawn McCarney                                                 int maxRetries)
364ab1327c3SLei YU {
365*770de580SShawn McCarney     std::unique_ptr<I2CDevice> dev(
366*770de580SShawn McCarney         new I2CDevice(busId, devAddr, initialState, maxRetries));
367ab1327c3SLei YU     return dev;
368ab1327c3SLei YU }
369ab1327c3SLei YU 
create(uint8_t busId,uint8_t devAddr,I2CInterface::InitialState initialState,int maxRetries)370d45a9a6dSShawn McCarney std::unique_ptr<I2CInterface> create(uint8_t busId, uint8_t devAddr,
371*770de580SShawn McCarney                                      I2CInterface::InitialState initialState,
372*770de580SShawn McCarney                                      int maxRetries)
373ab1327c3SLei YU {
374*770de580SShawn McCarney     return I2CDevice::create(busId, devAddr, initialState, maxRetries);
375ab1327c3SLei YU }
376ab1327c3SLei YU 
377ab1327c3SLei YU } // namespace i2c
378