xref: /openbmc/phosphor-power/tools/i2c/i2c.cpp (revision 770de580)
1 #include "i2c.hpp"
2 
3 #include <fcntl.h>
4 #include <sys/ioctl.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 
8 #include <cassert>
9 #include <cerrno>
10 
11 extern "C"
12 {
13 #include <i2c/smbus.h>
14 #include <linux/i2c-dev.h>
15 #include <linux/i2c.h>
16 }
17 
18 namespace i2c
19 {
20 
getFuncs()21 unsigned long I2CDevice::getFuncs()
22 {
23     // If functionality has not been cached
24     if (cachedFuncs == NO_FUNCS)
25     {
26         // Get functionality from adapter
27         int ret = 0, retries = 0;
28         do
29         {
30             ret = ioctl(fd, I2C_FUNCS, &cachedFuncs);
31         } while ((ret < 0) && (++retries <= maxRetries));
32 
33         if (ret < 0)
34         {
35             throw I2CException("Failed to get funcs", busStr, devAddr, errno);
36         }
37     }
38 
39     return cachedFuncs;
40 }
41 
checkReadFuncs(int type)42 void I2CDevice::checkReadFuncs(int type)
43 {
44     unsigned long funcs = getFuncs();
45     switch (type)
46     {
47         case I2C_SMBUS_BYTE:
48             if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
49             {
50                 throw I2CException("Missing SMBUS_READ_BYTE", busStr, devAddr);
51             }
52             break;
53         case I2C_SMBUS_BYTE_DATA:
54             if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
55             {
56                 throw I2CException("Missing SMBUS_READ_BYTE_DATA", busStr,
57                                    devAddr);
58             }
59             break;
60         case I2C_SMBUS_WORD_DATA:
61             if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
62             {
63                 throw I2CException("Missing SMBUS_READ_WORD_DATA", busStr,
64                                    devAddr);
65             }
66             break;
67         case I2C_SMBUS_BLOCK_DATA:
68             if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
69             {
70                 throw I2CException("Missing SMBUS_READ_BLOCK_DATA", busStr,
71                                    devAddr);
72             }
73             break;
74         case I2C_SMBUS_I2C_BLOCK_DATA:
75             if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
76             {
77                 throw I2CException("Missing I2C_FUNC_SMBUS_READ_I2C_BLOCK",
78                                    busStr, devAddr);
79             }
80             break;
81         default:
82             fprintf(stderr, "Unexpected read size type: %d\n", type);
83             assert(false);
84             break;
85     }
86 }
87 
checkWriteFuncs(int type)88 void I2CDevice::checkWriteFuncs(int type)
89 {
90     unsigned long funcs = getFuncs();
91     switch (type)
92     {
93         case I2C_SMBUS_BYTE:
94             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
95             {
96                 throw I2CException("Missing SMBUS_WRITE_BYTE", busStr, devAddr);
97             }
98             break;
99         case I2C_SMBUS_BYTE_DATA:
100             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
101             {
102                 throw I2CException("Missing SMBUS_WRITE_BYTE_DATA", busStr,
103                                    devAddr);
104             }
105             break;
106         case I2C_SMBUS_WORD_DATA:
107             if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
108             {
109                 throw I2CException("Missing SMBUS_WRITE_WORD_DATA", busStr,
110                                    devAddr);
111             }
112             break;
113         case I2C_SMBUS_BLOCK_DATA:
114             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
115             {
116                 throw I2CException("Missing SMBUS_WRITE_BLOCK_DATA", busStr,
117                                    devAddr);
118             }
119             break;
120         case I2C_SMBUS_I2C_BLOCK_DATA:
121             if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
122             {
123                 throw I2CException("Missing I2C_FUNC_SMBUS_WRITE_I2C_BLOCK",
124                                    busStr, devAddr);
125             }
126             break;
127         default:
128             fprintf(stderr, "Unexpected write size type: %d\n", type);
129             assert(false);
130     }
131 }
132 
open()133 void I2CDevice::open()
134 {
135     if (isOpen())
136     {
137         throw I2CException("Device already open", busStr, devAddr);
138     }
139 
140     int retries = 0;
141     do
142     {
143         fd = ::open(busStr.c_str(), O_RDWR);
144     } while ((fd == -1) && (++retries <= maxRetries));
145 
146     if (fd == -1)
147     {
148         throw I2CException("Failed to open", busStr, devAddr, errno);
149     }
150 
151     retries = 0;
152     int ret = 0;
153     do
154     {
155         ret = ioctl(fd, I2C_SLAVE, devAddr);
156     } while ((ret < 0) && (++retries <= maxRetries));
157 
158     if (ret < 0)
159     {
160         // Close device since setting slave address failed
161         closeWithoutException();
162 
163         throw I2CException("Failed to set I2C_SLAVE", busStr, devAddr, errno);
164     }
165 }
166 
close()167 void I2CDevice::close()
168 {
169     checkIsOpen();
170 
171     int ret = 0, retries = 0;
172     do
173     {
174         ret = ::close(fd);
175     } while ((ret == -1) && (++retries <= maxRetries));
176 
177     if (ret == -1)
178     {
179         throw I2CException("Failed to close", busStr, devAddr, errno);
180     }
181 
182     fd = INVALID_FD;
183     cachedFuncs = NO_FUNCS;
184 }
185 
read(uint8_t & data)186 void I2CDevice::read(uint8_t& data)
187 {
188     checkIsOpen();
189     checkReadFuncs(I2C_SMBUS_BYTE);
190 
191     int ret = 0, retries = 0;
192     do
193     {
194         ret = i2c_smbus_read_byte(fd);
195     } while ((ret < 0) && (++retries <= maxRetries));
196 
197     if (ret < 0)
198     {
199         throw I2CException("Failed to read byte", busStr, devAddr, errno);
200     }
201 
202     data = static_cast<uint8_t>(ret);
203 }
204 
read(uint8_t addr,uint8_t & data)205 void I2CDevice::read(uint8_t addr, uint8_t& data)
206 {
207     checkIsOpen();
208     checkReadFuncs(I2C_SMBUS_BYTE_DATA);
209 
210     int ret = 0, retries = 0;
211     do
212     {
213         ret = i2c_smbus_read_byte_data(fd, addr);
214     } while ((ret < 0) && (++retries <= maxRetries));
215 
216     if (ret < 0)
217     {
218         throw I2CException("Failed to read byte data", busStr, devAddr, errno);
219     }
220 
221     data = static_cast<uint8_t>(ret);
222 }
223 
read(uint8_t addr,uint16_t & data)224 void I2CDevice::read(uint8_t addr, uint16_t& data)
225 {
226     checkIsOpen();
227     checkReadFuncs(I2C_SMBUS_WORD_DATA);
228 
229     int ret = 0, retries = 0;
230     do
231     {
232         ret = i2c_smbus_read_word_data(fd, addr);
233     } while ((ret < 0) && (++retries <= maxRetries));
234 
235     if (ret < 0)
236     {
237         throw I2CException("Failed to read word data", busStr, devAddr, errno);
238     }
239 
240     data = static_cast<uint16_t>(ret);
241 }
242 
read(uint8_t addr,uint8_t & size,uint8_t * data,Mode mode)243 void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data, Mode mode)
244 {
245     checkIsOpen();
246 
247     int ret = -1, retries = 0;
248     switch (mode)
249     {
250         case Mode::SMBUS:
251             checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
252             do
253             {
254                 ret = i2c_smbus_read_block_data(fd, addr, data);
255             } while ((ret < 0) && (++retries <= maxRetries));
256             break;
257         case Mode::I2C:
258             checkReadFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
259             do
260             {
261                 ret = i2c_smbus_read_i2c_block_data(fd, addr, size, data);
262             } while ((ret < 0) && (++retries <= maxRetries));
263             if (ret != size)
264             {
265                 throw I2CException("Failed to read i2c block data", busStr,
266                                    devAddr, errno);
267             }
268             break;
269     }
270 
271     if (ret < 0)
272     {
273         throw I2CException("Failed to read block data", busStr, devAddr, errno);
274     }
275 
276     size = static_cast<uint8_t>(ret);
277 }
278 
write(uint8_t data)279 void I2CDevice::write(uint8_t data)
280 {
281     checkIsOpen();
282     checkWriteFuncs(I2C_SMBUS_BYTE);
283 
284     int ret = 0, retries = 0;
285     do
286     {
287         ret = i2c_smbus_write_byte(fd, data);
288     } while ((ret < 0) && (++retries <= maxRetries));
289 
290     if (ret < 0)
291     {
292         throw I2CException("Failed to write byte", busStr, devAddr, errno);
293     }
294 }
295 
write(uint8_t addr,uint8_t data)296 void I2CDevice::write(uint8_t addr, uint8_t data)
297 {
298     checkIsOpen();
299     checkWriteFuncs(I2C_SMBUS_BYTE_DATA);
300 
301     int ret = 0, retries = 0;
302     do
303     {
304         ret = i2c_smbus_write_byte_data(fd, addr, data);
305     } while ((ret < 0) && (++retries <= maxRetries));
306 
307     if (ret < 0)
308     {
309         throw I2CException("Failed to write byte data", busStr, devAddr, errno);
310     }
311 }
312 
write(uint8_t addr,uint16_t data)313 void I2CDevice::write(uint8_t addr, uint16_t data)
314 {
315     checkIsOpen();
316     checkWriteFuncs(I2C_SMBUS_WORD_DATA);
317 
318     int ret = 0, retries = 0;
319     do
320     {
321         ret = i2c_smbus_write_word_data(fd, addr, data);
322     } while ((ret < 0) && (++retries <= maxRetries));
323 
324     if (ret < 0)
325     {
326         throw I2CException("Failed to write word data", busStr, devAddr, errno);
327     }
328 }
329 
write(uint8_t addr,uint8_t size,const uint8_t * data,Mode mode)330 void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data,
331                       Mode mode)
332 {
333     checkIsOpen();
334 
335     int ret = -1, retries = 0;
336     switch (mode)
337     {
338         case Mode::SMBUS:
339             checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
340             do
341             {
342                 ret = i2c_smbus_write_block_data(fd, addr, size, data);
343             } while ((ret < 0) && (++retries <= maxRetries));
344             break;
345         case Mode::I2C:
346             checkWriteFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
347             do
348             {
349                 ret = i2c_smbus_write_i2c_block_data(fd, addr, size, data);
350             } while ((ret < 0) && (++retries <= maxRetries));
351             break;
352     }
353 
354     if (ret < 0)
355     {
356         throw I2CException("Failed to write block data", busStr, devAddr,
357                            errno);
358     }
359 }
360 
create(uint8_t busId,uint8_t devAddr,InitialState initialState,int maxRetries)361 std::unique_ptr<I2CInterface> I2CDevice::create(uint8_t busId, uint8_t devAddr,
362                                                 InitialState initialState,
363                                                 int maxRetries)
364 {
365     std::unique_ptr<I2CDevice> dev(
366         new I2CDevice(busId, devAddr, initialState, maxRetries));
367     return dev;
368 }
369 
create(uint8_t busId,uint8_t devAddr,I2CInterface::InitialState initialState,int maxRetries)370 std::unique_ptr<I2CInterface> create(uint8_t busId, uint8_t devAddr,
371                                      I2CInterface::InitialState initialState,
372                                      int maxRetries)
373 {
374     return I2CDevice::create(busId, devAddr, initialState, maxRetries);
375 }
376 
377 } // namespace i2c
378