dpdk: Add support for Mellanox ConnectX-4 devices
[vpp.git] / src / vlib / i2c.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vlib/vlib.h>
17 #include <vlib/i2c.h>
18
19 static inline void
20 i2c_delay (i2c_bus_t * b, f64 timeout)
21 {
22   vlib_main_t *vm = vlib_get_main ();
23   vlib_time_wait (vm, timeout);
24 }
25
26 static void
27 i2c_wait_for_scl (i2c_bus_t * b)
28 {
29   f64 t = 0;
30
31   while (t < b->hold_time)
32     {
33       int sda, scl;
34       i2c_delay (b, b->rise_fall_time);
35       b->get_bits (b, &scl, &sda);
36
37       if (scl)
38         return;
39
40       t += b->rise_fall_time;
41     }
42   b->timeout = 1;
43 }
44
45 static void
46 i2c_start (i2c_bus_t * b)
47 {
48   b->timeout = 0;
49
50   b->put_bits (b, 1, 1);
51   i2c_wait_for_scl (b);
52
53   if (vlib_i2c_bus_timed_out (b))
54     return;
55
56   b->put_bits (b, 1, 0);
57   i2c_delay (b, b->hold_time);
58   b->put_bits (b, 0, 0);
59   i2c_delay (b, b->hold_time);
60 }
61
62 static void
63 i2c_stop (i2c_bus_t * b)
64 {
65   b->put_bits (b, 0, 0);
66   i2c_delay (b, b->rise_fall_time);
67
68   b->put_bits (b, 1, 0);
69   i2c_delay (b, b->hold_time);
70
71   b->put_bits (b, 1, 1);
72   i2c_delay (b, b->hold_time);
73 }
74
75 static void
76 i2c_write_bit (i2c_bus_t * b, int sda)
77 {
78   b->put_bits (b, 0, sda);
79   i2c_delay (b, b->rise_fall_time);
80
81   b->put_bits (b, 1, sda);
82   i2c_wait_for_scl (b);
83   i2c_delay (b, b->hold_time);
84
85   b->put_bits (b, 0, sda);
86   i2c_delay (b, b->rise_fall_time);
87 }
88
89 static void
90 i2c_read_bit (i2c_bus_t * b, int *sda)
91 {
92   int scl;
93
94   b->put_bits (b, 1, 1);
95   i2c_wait_for_scl (b);
96   i2c_delay (b, b->hold_time);
97
98   b->get_bits (b, &scl, sda);
99
100   b->put_bits (b, 0, 1);
101   i2c_delay (b, b->rise_fall_time);
102 }
103
104 static void
105 i2c_write_byte (i2c_bus_t * b, u8 data)
106 {
107   int i, sda;
108
109   for (i = 7; i >= 0; i--)
110     {
111       i2c_write_bit (b, (data >> i) & 1);
112       if (b->timeout)
113         return;
114     }
115
116   b->put_bits (b, 0, 1);
117   i2c_delay (b, b->rise_fall_time);
118
119   i2c_read_bit (b, &sda);
120
121   if (sda)
122     b->timeout = 1;
123 }
124
125
126 static void
127 i2c_read_byte (i2c_bus_t * b, u8 * data, int ack)
128 {
129   int i, sda;
130
131   *data = 0;
132
133   b->put_bits (b, 0, 1);
134   i2c_delay (b, b->rise_fall_time);
135
136   for (i = 7; i >= 0; i--)
137     {
138       i2c_read_bit (b, &sda);
139       if (b->timeout)
140         return;
141
142       *data |= (sda != 0) << i;
143     }
144
145   i2c_write_bit (b, ack == 0);
146 }
147
148
149 void
150 vlib_i2c_init (i2c_bus_t * b)
151 {
152   f64 tick;
153   if (!b->clock)
154     b->clock = 400000;
155
156   tick = 1.0 / b->clock;
157
158   /* Spend 40% of time in low and high states */
159   if (!b->hold_time)
160     b->hold_time = 0.4 * tick;
161
162   /* Spend 10% of time waiting for rise and fall */
163   if (!b->rise_fall_time)
164     b->rise_fall_time = 0.1 * tick;
165 }
166
167 void
168 vlib_i2c_xfer (i2c_bus_t * bus, i2c_msg_t * msgs)
169 {
170   i2c_msg_t *msg;
171   int i;
172
173   vec_foreach (msg, msgs)
174   {
175     i2c_start (bus);
176     i2c_write_byte (bus,
177                     (msg->addr << 1) + (msg->flags == I2C_MSG_FLAG_READ));
178
179     if (msg->flags & I2C_MSG_FLAG_READ)
180       for (i = 0; i < msg->len; i++)
181         {
182           i2c_read_byte (bus, &msg->buffer[i], /* ack */ i + 1 != msg->len);
183           if (bus->timeout)
184             goto done;
185         }
186
187     else
188       for (i = 0; i < msg->len; i++)
189         {
190           i2c_write_byte (bus, msg->buffer[i]);
191           if (bus->timeout)
192             goto done;
193         }
194   }
195
196 done:
197   i2c_stop (bus);
198 }
199
200 void
201 vlib_i2c_read_eeprom (i2c_bus_t * bus, u8 i2c_addr, u16 start_addr,
202                       u16 length, u8 * data)
203 {
204   i2c_msg_t *msg = 0;
205   u8 start_address[1];
206
207   vec_validate (msg, 1);
208
209   start_address[0] = start_addr;
210   msg[0].addr = i2c_addr;
211   msg[0].flags = I2C_MSG_FLAG_WRITE;
212   msg[0].buffer = (u8 *) & start_address;
213   msg[0].len = 1;
214
215   msg[1].addr = i2c_addr;
216   msg[1].flags = I2C_MSG_FLAG_READ;
217   msg[1].buffer = data;
218   msg[1].len = length;
219
220   vlib_i2c_xfer (bus, msg);
221
222   vec_free (msg);
223 }
224
225 /*
226  * fd.io coding-style-patch-verification: ON
227  *
228  * Local Variables:
229  * eval: (c-set-style "gnu")
230  * End:
231  */