1 /*
2 * Copyright 2012-15 Advanced Micro Devices, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: AMD
23 *
24 */
25
26 #include "dm_services.h"
27
28 /*
29 * Pre-requisites: headers required by header of this unit
30 */
31 #include "include/i2caux_interface.h"
32 #include "engine.h"
33 #include "i2c_engine.h"
34 #include "i2c_hw_engine.h"
35
36 /*
37 * Header of this unit
38 */
39
40 #include "i2c_generic_hw_engine.h"
41
42 /*
43 * Post-requisites: headers required by this unit
44 */
45
46 /*
47 * This unit
48 */
49
50 /*
51 * @brief
52 * Cast 'struct i2c_hw_engine *'
53 * to 'struct i2c_generic_hw_engine *'
54 */
55 #define FROM_I2C_HW_ENGINE(ptr) \
56 container_of((ptr), struct i2c_generic_hw_engine, base)
57
58 /*
59 * @brief
60 * Cast 'struct i2c_engine *'
61 * to 'struct i2c_generic_hw_engine *'
62 */
63 #define FROM_I2C_ENGINE(ptr) \
64 FROM_I2C_HW_ENGINE(container_of((ptr), struct i2c_hw_engine, base))
65
66 /*
67 * @brief
68 * Cast 'struct engine *'
69 * to 'struct i2c_generic_hw_engine *'
70 */
71 #define FROM_ENGINE(ptr) \
72 FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base))
73
dal_i2c_generic_hw_engine_get_engine_type(const struct engine * engine)74 enum i2caux_engine_type dal_i2c_generic_hw_engine_get_engine_type(
75 const struct engine *engine)
76 {
77 return I2CAUX_ENGINE_TYPE_I2C_GENERIC_HW;
78 }
79
80 /*
81 * @brief
82 * Single transaction handling.
83 * Since transaction may be bigger than HW buffer size,
84 * it divides transaction to sub-transactions
85 * and uses batch transaction feature of the engine.
86 */
dal_i2c_generic_hw_engine_submit_request(struct engine * engine,struct i2caux_transaction_request * i2caux_request,bool middle_of_transaction)87 bool dal_i2c_generic_hw_engine_submit_request(
88 struct engine *engine,
89 struct i2caux_transaction_request *i2caux_request,
90 bool middle_of_transaction)
91 {
92 struct i2c_generic_hw_engine *hw_engine = FROM_ENGINE(engine);
93
94 struct i2c_hw_engine *base = &hw_engine->base;
95
96 uint32_t max_payload_size =
97 base->funcs->get_hw_buffer_available_size(base);
98
99 bool initial_stop_bit = !middle_of_transaction;
100
101 struct i2c_generic_transaction_attributes attributes;
102
103 enum i2c_channel_operation_result operation_result =
104 I2C_CHANNEL_OPERATION_FAILED;
105
106 bool result = false;
107
108 /* setup transaction initial properties */
109
110 uint8_t address = i2caux_request->payload.address;
111 uint8_t *current_payload = i2caux_request->payload.data;
112 uint32_t remaining_payload_size = i2caux_request->payload.length;
113
114 bool first_iteration = true;
115
116 if (i2caux_request->operation == I2CAUX_TRANSACTION_READ)
117 attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_READ;
118 else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE)
119 attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_WRITE;
120 else {
121 i2caux_request->status =
122 I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION;
123 return false;
124 }
125
126 /* Do batch transaction.
127 * Divide read/write data into payloads which fit HW buffer size.
128 * 1. Single transaction:
129 * start_bit = 1, stop_bit depends on session state, ack_on_read = 0;
130 * 2. Start of batch transaction:
131 * start_bit = 1, stop_bit = 0, ack_on_read = 1;
132 * 3. Middle of batch transaction:
133 * start_bit = 0, stop_bit = 0, ack_on_read = 1;
134 * 4. End of batch transaction:
135 * start_bit = 0, stop_bit depends on session state, ack_on_read = 0.
136 * Session stop bit is set if 'middle_of_transaction' = 0. */
137
138 while (remaining_payload_size) {
139 uint32_t current_transaction_size;
140 uint32_t current_payload_size;
141
142 bool last_iteration;
143 bool stop_bit;
144
145 /* Calculate current transaction size and payload size.
146 * Transaction size = total number of bytes in transaction,
147 * including slave's address;
148 * Payload size = number of data bytes in transaction. */
149
150 if (first_iteration) {
151 /* In the first sub-transaction we send slave's address
152 * thus we need to reserve one byte for it */
153 current_transaction_size =
154 (remaining_payload_size > max_payload_size - 1) ?
155 max_payload_size :
156 remaining_payload_size + 1;
157
158 current_payload_size = current_transaction_size - 1;
159 } else {
160 /* Second and further sub-transactions will have
161 * entire buffer reserved for data */
162 current_transaction_size =
163 (remaining_payload_size > max_payload_size) ?
164 max_payload_size :
165 remaining_payload_size;
166
167 current_payload_size = current_transaction_size;
168 }
169
170 last_iteration =
171 (remaining_payload_size == current_payload_size);
172
173 stop_bit = last_iteration ? initial_stop_bit : false;
174
175 /* write slave device address */
176
177 if (first_iteration)
178 hw_engine->funcs->write_address(hw_engine, address);
179
180 /* write current portion of data, if requested */
181
182 if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE)
183 hw_engine->funcs->write_data(
184 hw_engine,
185 current_payload,
186 current_payload_size);
187
188 /* execute transaction */
189
190 attributes.start_bit = first_iteration;
191 attributes.stop_bit = stop_bit;
192 attributes.last_read = last_iteration;
193 attributes.transaction_size = current_transaction_size;
194
195 hw_engine->funcs->execute_transaction(hw_engine, &attributes);
196
197 /* wait until transaction is processed; if it fails - quit */
198
199 operation_result = base->funcs->wait_on_operation_result(
200 base,
201 base->funcs->get_transaction_timeout(
202 base, current_transaction_size),
203 I2C_CHANNEL_OPERATION_ENGINE_BUSY);
204
205 if (operation_result != I2C_CHANNEL_OPERATION_SUCCEEDED)
206 break;
207
208 /* read current portion of data, if requested */
209
210 /* the read offset should be 1 for first sub-transaction,
211 * and 0 for any next one */
212
213 if (i2caux_request->operation == I2CAUX_TRANSACTION_READ)
214 hw_engine->funcs->read_data(hw_engine, current_payload,
215 current_payload_size, first_iteration ? 1 : 0);
216
217 /* update loop variables */
218
219 first_iteration = false;
220 current_payload += current_payload_size;
221 remaining_payload_size -= current_payload_size;
222 }
223
224 /* update transaction status */
225
226 switch (operation_result) {
227 case I2C_CHANNEL_OPERATION_SUCCEEDED:
228 i2caux_request->status =
229 I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
230 result = true;
231 break;
232 case I2C_CHANNEL_OPERATION_NO_RESPONSE:
233 i2caux_request->status =
234 I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
235 break;
236 case I2C_CHANNEL_OPERATION_TIMEOUT:
237 i2caux_request->status =
238 I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
239 break;
240 case I2C_CHANNEL_OPERATION_FAILED:
241 i2caux_request->status =
242 I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE;
243 break;
244 default:
245 i2caux_request->status =
246 I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION;
247 }
248
249 return result;
250 }
251
252 /*
253 * @brief
254 * Returns number of microseconds to wait until timeout to be considered
255 */
dal_i2c_generic_hw_engine_get_transaction_timeout(const struct i2c_hw_engine * engine,uint32_t length)256 uint32_t dal_i2c_generic_hw_engine_get_transaction_timeout(
257 const struct i2c_hw_engine *engine,
258 uint32_t length)
259 {
260 const struct i2c_engine *base = &engine->base;
261
262 uint32_t speed = base->funcs->get_speed(base);
263
264 if (!speed)
265 return 0;
266
267 /* total timeout = period_timeout * (start + data bits count + stop) */
268
269 return ((1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed) *
270 (1 + (length << 3) + 1);
271 }
272
dal_i2c_generic_hw_engine_construct(struct i2c_generic_hw_engine * engine,struct dc_context * ctx)273 void dal_i2c_generic_hw_engine_construct(
274 struct i2c_generic_hw_engine *engine,
275 struct dc_context *ctx)
276 {
277 dal_i2c_hw_engine_construct(&engine->base, ctx);
278 }
279
dal_i2c_generic_hw_engine_destruct(struct i2c_generic_hw_engine * engine)280 void dal_i2c_generic_hw_engine_destruct(
281 struct i2c_generic_hw_engine *engine)
282 {
283 dal_i2c_hw_engine_destruct(&engine->base);
284 }
285