]>
Commit | Line | Data |
---|---|---|
4562236b HW |
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 | #include "include/logger_interface.h" | |
28 | /* | |
29 | * Pre-requisites: headers required by header of this unit | |
30 | */ | |
31 | ||
32 | #include "include/i2caux_interface.h" | |
33 | #include "../engine.h" | |
34 | #include "../i2c_engine.h" | |
35 | #include "../i2c_hw_engine.h" | |
36 | #include "../i2c_generic_hw_engine.h" | |
37 | /* | |
38 | * Header of this unit | |
39 | */ | |
40 | ||
41 | #include "i2c_hw_engine_dce110.h" | |
42 | ||
43 | /* | |
44 | * Post-requisites: headers required by this unit | |
45 | */ | |
46 | #include "reg_helper.h" | |
47 | ||
48 | /* | |
49 | * This unit | |
50 | */ | |
51 | ||
52 | enum dc_i2c_status { | |
53 | DC_I2C_STATUS__DC_I2C_STATUS_IDLE, | |
54 | DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW, | |
55 | DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_HW | |
56 | }; | |
57 | ||
58 | enum dc_i2c_arbitration { | |
59 | DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL, | |
60 | DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_HIGH | |
61 | }; | |
62 | ||
63 | enum { | |
64 | /* No timeout in HW | |
65 | * (timeout implemented in SW by querying status) */ | |
66 | I2C_SETUP_TIME_LIMIT = 255, | |
67 | I2C_HW_BUFFER_SIZE = 538 | |
68 | }; | |
69 | ||
70 | /* | |
71 | * @brief | |
72 | * Cast pointer to 'struct i2c_hw_engine *' | |
73 | * to pointer 'struct i2c_hw_engine_dce110 *' | |
74 | */ | |
75 | #define FROM_I2C_HW_ENGINE(ptr) \ | |
76 | container_of((ptr), struct i2c_hw_engine_dce110, base) | |
77 | /* | |
78 | * @brief | |
79 | * Cast pointer to 'struct i2c_engine *' | |
80 | * to pointer to 'struct i2c_hw_engine_dce110 *' | |
81 | */ | |
82 | #define FROM_I2C_ENGINE(ptr) \ | |
83 | FROM_I2C_HW_ENGINE(container_of((ptr), struct i2c_hw_engine, base)) | |
84 | ||
85 | /* | |
86 | * @brief | |
87 | * Cast pointer to 'struct engine *' | |
88 | * to 'pointer to struct i2c_hw_engine_dce110 *' | |
89 | */ | |
90 | #define FROM_ENGINE(ptr) \ | |
91 | FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) | |
92 | ||
93 | #define CTX \ | |
94 | hw_engine->base.base.base.ctx | |
95 | ||
96 | #define REG(reg_name)\ | |
97 | (hw_engine->regs->reg_name) | |
98 | ||
99 | #undef FN | |
100 | #define FN(reg_name, field_name) \ | |
101 | hw_engine->i2c_shift->field_name, hw_engine->i2c_mask->field_name | |
102 | ||
103 | #include "reg_helper.h" | |
104 | ||
105 | static void disable_i2c_hw_engine( | |
106 | struct i2c_hw_engine_dce110 *hw_engine) | |
107 | { | |
108 | REG_UPDATE_N(SETUP, 1, FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE), 0); | |
109 | } | |
110 | ||
111 | static void release_engine( | |
112 | struct engine *engine) | |
113 | { | |
114 | struct i2c_hw_engine_dce110 *hw_engine = FROM_ENGINE(engine); | |
115 | ||
116 | struct i2c_engine *base = NULL; | |
117 | bool safe_to_reset; | |
118 | ||
119 | base = &hw_engine->base.base; | |
120 | ||
121 | /* Restore original HW engine speed */ | |
122 | ||
123 | base->funcs->set_speed(base, hw_engine->base.original_speed); | |
124 | ||
125 | /* Release I2C */ | |
126 | REG_UPDATE(DC_I2C_ARBITRATION, DC_I2C_SW_DONE_USING_I2C_REG, 1); | |
127 | ||
128 | /* Reset HW engine */ | |
129 | { | |
130 | uint32_t i2c_sw_status = 0; | |
131 | REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); | |
132 | /* if used by SW, safe to reset */ | |
133 | safe_to_reset = (i2c_sw_status == 1); | |
134 | } | |
135 | ||
bf5563ed DA |
136 | if (safe_to_reset) |
137 | REG_UPDATE_2( | |
138 | DC_I2C_CONTROL, | |
139 | DC_I2C_SOFT_RESET, 1, | |
140 | DC_I2C_SW_STATUS_RESET, 1); | |
141 | else | |
142 | REG_UPDATE(DC_I2C_CONTROL, DC_I2C_SW_STATUS_RESET, 1); | |
4562236b HW |
143 | |
144 | /* HW I2c engine - clock gating feature */ | |
145 | if (!hw_engine->engine_keep_power_up_count) | |
146 | disable_i2c_hw_engine(hw_engine); | |
147 | } | |
148 | ||
149 | static bool setup_engine( | |
150 | struct i2c_engine *i2c_engine) | |
151 | { | |
152 | struct i2c_hw_engine_dce110 *hw_engine = FROM_I2C_ENGINE(i2c_engine); | |
153 | ||
154 | /* Program pin select */ | |
155 | REG_UPDATE_6( | |
156 | DC_I2C_CONTROL, | |
157 | DC_I2C_GO, 0, | |
158 | DC_I2C_SOFT_RESET, 0, | |
159 | DC_I2C_SEND_RESET, 0, | |
160 | DC_I2C_SW_STATUS_RESET, 1, | |
161 | DC_I2C_TRANSACTION_COUNT, 0, | |
162 | DC_I2C_DDC_SELECT, hw_engine->engine_id); | |
163 | ||
164 | /* Program time limit */ | |
165 | REG_UPDATE_N( | |
166 | SETUP, 2, | |
167 | FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_TIME_LIMIT), I2C_SETUP_TIME_LIMIT, | |
168 | FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE), 1); | |
169 | ||
170 | /* Program HW priority | |
171 | * set to High - interrupt software I2C at any time | |
172 | * Enable restart of SW I2C that was interrupted by HW | |
173 | * disable queuing of software while I2C is in use by HW */ | |
174 | REG_UPDATE_2( | |
175 | DC_I2C_ARBITRATION, | |
176 | DC_I2C_NO_QUEUED_SW_GO, 0, | |
177 | DC_I2C_SW_PRIORITY, DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL); | |
178 | ||
179 | return true; | |
180 | } | |
181 | ||
182 | static uint32_t get_speed( | |
183 | const struct i2c_engine *i2c_engine) | |
184 | { | |
185 | const struct i2c_hw_engine_dce110 *hw_engine = FROM_I2C_ENGINE(i2c_engine); | |
186 | uint32_t pre_scale = 0; | |
187 | ||
188 | REG_GET(SPEED, DC_I2C_DDC1_PRESCALE, &pre_scale); | |
189 | ||
190 | /* [anaumov] it seems following is unnecessary */ | |
191 | /*ASSERT(value.bits.DC_I2C_DDC1_PRESCALE);*/ | |
192 | return pre_scale ? | |
193 | hw_engine->reference_frequency / pre_scale : | |
194 | hw_engine->base.default_speed; | |
195 | } | |
196 | ||
197 | static void set_speed( | |
198 | struct i2c_engine *i2c_engine, | |
199 | uint32_t speed) | |
200 | { | |
201 | struct i2c_hw_engine_dce110 *hw_engine = FROM_I2C_ENGINE(i2c_engine); | |
202 | ||
203 | if (speed) { | |
204 | if (hw_engine->i2c_mask->DC_I2C_DDC1_START_STOP_TIMING_CNTL) | |
205 | REG_UPDATE_N( | |
206 | SPEED, 3, | |
207 | FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE), hw_engine->reference_frequency / speed, | |
208 | FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD), 2, | |
209 | FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_START_STOP_TIMING_CNTL), speed > 50 ? 2:1); | |
210 | else | |
211 | REG_UPDATE_N( | |
212 | SPEED, 2, | |
213 | FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE), hw_engine->reference_frequency / speed, | |
214 | FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD), 2); | |
215 | } | |
216 | } | |
217 | ||
218 | static inline void reset_hw_engine(struct engine *engine) | |
219 | { | |
220 | struct i2c_hw_engine_dce110 *hw_engine = FROM_ENGINE(engine); | |
221 | ||
222 | REG_UPDATE_2( | |
223 | DC_I2C_CONTROL, | |
224 | DC_I2C_SW_STATUS_RESET, 1, | |
225 | DC_I2C_SW_STATUS_RESET, 1); | |
226 | } | |
227 | ||
228 | static bool is_hw_busy(struct engine *engine) | |
229 | { | |
230 | struct i2c_hw_engine_dce110 *hw_engine = FROM_ENGINE(engine); | |
231 | uint32_t i2c_sw_status = 0; | |
232 | ||
233 | REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); | |
234 | if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_IDLE) | |
235 | return false; | |
236 | ||
237 | reset_hw_engine(engine); | |
238 | ||
239 | REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); | |
240 | return i2c_sw_status != DC_I2C_STATUS__DC_I2C_STATUS_IDLE; | |
241 | } | |
242 | ||
243 | ||
244 | #define STOP_TRANS_PREDICAT \ | |
245 | ((hw_engine->transaction_count == 3) || \ | |
246 | (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || \ | |
247 | (request->action & I2CAUX_TRANSACTION_ACTION_I2C_READ)) | |
248 | ||
249 | #define SET_I2C_TRANSACTION(id) \ | |
250 | do { \ | |
251 | REG_UPDATE_N(DC_I2C_TRANSACTION##id, 5, \ | |
252 | FN(DC_I2C_TRANSACTION0, DC_I2C_STOP_ON_NACK0), 1, \ | |
253 | FN(DC_I2C_TRANSACTION0, DC_I2C_START0), 1, \ | |
254 | FN(DC_I2C_TRANSACTION0, DC_I2C_STOP0), STOP_TRANS_PREDICAT ? 1:0, \ | |
255 | FN(DC_I2C_TRANSACTION0, DC_I2C_RW0), (0 != (request->action & I2CAUX_TRANSACTION_ACTION_I2C_READ)), \ | |
256 | FN(DC_I2C_TRANSACTION0, DC_I2C_COUNT0), length); \ | |
257 | if (STOP_TRANS_PREDICAT) \ | |
258 | last_transaction = true; \ | |
259 | } while (false) | |
260 | ||
261 | ||
262 | static bool process_transaction( | |
263 | struct i2c_hw_engine_dce110 *hw_engine, | |
264 | struct i2c_request_transaction_data *request) | |
265 | { | |
266 | uint32_t length = request->length; | |
267 | uint8_t *buffer = request->data; | |
268 | uint32_t value = 0; | |
269 | ||
270 | bool last_transaction = false; | |
271 | ||
272 | struct dc_context *ctx = NULL; | |
273 | ||
274 | ctx = hw_engine->base.base.base.ctx; | |
275 | ||
276 | ||
277 | ||
278 | switch (hw_engine->transaction_count) { | |
279 | case 0: | |
280 | SET_I2C_TRANSACTION(0); | |
281 | break; | |
282 | case 1: | |
283 | SET_I2C_TRANSACTION(1); | |
284 | break; | |
285 | case 2: | |
286 | SET_I2C_TRANSACTION(2); | |
287 | break; | |
288 | case 3: | |
289 | SET_I2C_TRANSACTION(3); | |
290 | break; | |
291 | default: | |
292 | /* TODO Warning ? */ | |
293 | break; | |
294 | } | |
295 | ||
296 | ||
297 | /* Write the I2C address and I2C data | |
298 | * into the hardware circular buffer, one byte per entry. | |
299 | * As an example, the 7-bit I2C slave address for CRT monitor | |
300 | * for reading DDC/EDID information is 0b1010001. | |
301 | * For an I2C send operation, the LSB must be programmed to 0; | |
302 | * for I2C receive operation, the LSB must be programmed to 1. */ | |
303 | if (hw_engine->transaction_count == 0) { | |
bf5563ed DA |
304 | value = REG_SET_4(DC_I2C_DATA, 0, |
305 | DC_I2C_DATA_RW, false, | |
306 | DC_I2C_DATA, request->address, | |
307 | DC_I2C_INDEX, 0, | |
308 | DC_I2C_INDEX_WRITE, 1); | |
4562236b HW |
309 | hw_engine->buffer_used_write = 0; |
310 | } else | |
bf5563ed DA |
311 | value = REG_SET_2(DC_I2C_DATA, 0, |
312 | DC_I2C_DATA_RW, false, | |
313 | DC_I2C_DATA, request->address); | |
4562236b HW |
314 | |
315 | hw_engine->buffer_used_write++; | |
316 | ||
317 | if (!(request->action & I2CAUX_TRANSACTION_ACTION_I2C_READ)) { | |
318 | while (length) { | |
319 | REG_SET_2(DC_I2C_DATA, value, | |
320 | DC_I2C_INDEX_WRITE, 0, | |
321 | DC_I2C_DATA, *buffer++); | |
322 | hw_engine->buffer_used_write++; | |
323 | --length; | |
324 | } | |
325 | } | |
326 | ||
327 | ++hw_engine->transaction_count; | |
328 | hw_engine->buffer_used_bytes += length + 1; | |
329 | ||
330 | return last_transaction; | |
331 | } | |
332 | ||
333 | static void execute_transaction( | |
334 | struct i2c_hw_engine_dce110 *hw_engine) | |
335 | { | |
336 | REG_UPDATE_N(SETUP, 5, | |
337 | FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_EN), 0, | |
338 | FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_CLK_DRIVE_EN), 0, | |
339 | FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_SEL), 0, | |
340 | FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_TRANSACTION_DELAY), 0, | |
341 | FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_BYTE_DELAY), 0); | |
342 | ||
343 | ||
344 | REG_UPDATE_5(DC_I2C_CONTROL, | |
345 | DC_I2C_SOFT_RESET, 0, | |
346 | DC_I2C_SW_STATUS_RESET, 0, | |
347 | DC_I2C_SEND_RESET, 0, | |
348 | DC_I2C_GO, 0, | |
349 | DC_I2C_TRANSACTION_COUNT, hw_engine->transaction_count - 1); | |
350 | ||
351 | /* start I2C transfer */ | |
352 | REG_UPDATE(DC_I2C_CONTROL, DC_I2C_GO, 1); | |
353 | ||
354 | /* all transactions were executed and HW buffer became empty | |
355 | * (even though it actually happens when status becomes DONE) */ | |
356 | hw_engine->transaction_count = 0; | |
357 | hw_engine->buffer_used_bytes = 0; | |
358 | } | |
359 | ||
360 | static void submit_channel_request( | |
361 | struct i2c_engine *engine, | |
362 | struct i2c_request_transaction_data *request) | |
363 | { | |
364 | request->status = I2C_CHANNEL_OPERATION_SUCCEEDED; | |
365 | ||
366 | if (!process_transaction(FROM_I2C_ENGINE(engine), request)) | |
367 | return; | |
368 | ||
369 | if (is_hw_busy(&engine->base)) { | |
370 | request->status = I2C_CHANNEL_OPERATION_ENGINE_BUSY; | |
371 | return; | |
372 | } | |
373 | ||
374 | execute_transaction(FROM_I2C_ENGINE(engine)); | |
375 | } | |
376 | ||
377 | static void process_channel_reply( | |
378 | struct i2c_engine *engine, | |
379 | struct i2c_reply_transaction_data *reply) | |
380 | { | |
381 | uint32_t length = reply->length; | |
382 | uint8_t *buffer = reply->data; | |
383 | ||
384 | struct i2c_hw_engine_dce110 *hw_engine = | |
385 | FROM_I2C_ENGINE(engine); | |
386 | ||
387 | ||
388 | REG_SET_3(DC_I2C_DATA, 0, | |
389 | DC_I2C_INDEX, hw_engine->buffer_used_write, | |
390 | DC_I2C_DATA_RW, 1, | |
391 | DC_I2C_INDEX_WRITE, 1); | |
392 | ||
393 | while (length) { | |
394 | /* after reading the status, | |
395 | * if the I2C operation executed successfully | |
396 | * (i.e. DC_I2C_STATUS_DONE = 1) then the I2C controller | |
397 | * should read data bytes from I2C circular data buffer */ | |
398 | ||
399 | uint32_t i2c_data; | |
400 | ||
401 | REG_GET(DC_I2C_DATA, DC_I2C_DATA, &i2c_data); | |
402 | *buffer++ = i2c_data; | |
403 | ||
404 | --length; | |
405 | } | |
406 | } | |
407 | ||
408 | static enum i2c_channel_operation_result get_channel_status( | |
409 | struct i2c_engine *i2c_engine, | |
410 | uint8_t *returned_bytes) | |
411 | { | |
412 | uint32_t i2c_sw_status = 0; | |
413 | struct i2c_hw_engine_dce110 *hw_engine = FROM_I2C_ENGINE(i2c_engine); | |
414 | uint32_t value = | |
415 | REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); | |
416 | ||
417 | if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW) | |
418 | return I2C_CHANNEL_OPERATION_ENGINE_BUSY; | |
419 | else if (value & hw_engine->i2c_mask->DC_I2C_SW_STOPPED_ON_NACK) | |
420 | return I2C_CHANNEL_OPERATION_NO_RESPONSE; | |
421 | else if (value & hw_engine->i2c_mask->DC_I2C_SW_TIMEOUT) | |
422 | return I2C_CHANNEL_OPERATION_TIMEOUT; | |
423 | else if (value & hw_engine->i2c_mask->DC_I2C_SW_ABORTED) | |
424 | return I2C_CHANNEL_OPERATION_FAILED; | |
425 | else if (value & hw_engine->i2c_mask->DC_I2C_SW_DONE) | |
426 | return I2C_CHANNEL_OPERATION_SUCCEEDED; | |
427 | ||
428 | /* | |
429 | * this is the case when HW used for communication, I2C_SW_STATUS | |
430 | * could be zero | |
431 | */ | |
432 | return I2C_CHANNEL_OPERATION_SUCCEEDED; | |
433 | } | |
434 | ||
435 | static uint32_t get_hw_buffer_available_size( | |
436 | const struct i2c_hw_engine *engine) | |
437 | { | |
438 | return I2C_HW_BUFFER_SIZE - | |
439 | FROM_I2C_HW_ENGINE(engine)->buffer_used_bytes; | |
440 | } | |
441 | ||
442 | static uint32_t get_transaction_timeout( | |
443 | const struct i2c_hw_engine *engine, | |
444 | uint32_t length) | |
445 | { | |
446 | uint32_t speed = engine->base.funcs->get_speed(&engine->base); | |
447 | ||
448 | uint32_t period_timeout; | |
449 | uint32_t num_of_clock_stretches; | |
450 | ||
451 | if (!speed) | |
452 | return 0; | |
453 | ||
454 | period_timeout = (1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed; | |
455 | ||
456 | num_of_clock_stretches = 1 + (length << 3) + 1; | |
457 | num_of_clock_stretches += | |
458 | (FROM_I2C_HW_ENGINE(engine)->buffer_used_bytes << 3) + | |
459 | (FROM_I2C_HW_ENGINE(engine)->transaction_count << 1); | |
460 | ||
461 | return period_timeout * num_of_clock_stretches; | |
462 | } | |
463 | ||
464 | static void destroy( | |
465 | struct i2c_engine **i2c_engine) | |
466 | { | |
467 | struct i2c_hw_engine_dce110 *engine_dce110 = | |
468 | FROM_I2C_ENGINE(*i2c_engine); | |
469 | ||
470 | dal_i2c_hw_engine_destruct(&engine_dce110->base); | |
471 | ||
2004f45e | 472 | kfree(engine_dce110); |
4562236b HW |
473 | |
474 | *i2c_engine = NULL; | |
475 | } | |
476 | ||
477 | static const struct i2c_engine_funcs i2c_engine_funcs = { | |
478 | .destroy = destroy, | |
479 | .get_speed = get_speed, | |
480 | .set_speed = set_speed, | |
481 | .setup_engine = setup_engine, | |
482 | .submit_channel_request = submit_channel_request, | |
483 | .process_channel_reply = process_channel_reply, | |
484 | .get_channel_status = get_channel_status, | |
485 | .acquire_engine = dal_i2c_hw_engine_acquire_engine, | |
486 | }; | |
487 | ||
488 | static const struct engine_funcs engine_funcs = { | |
489 | .release_engine = release_engine, | |
490 | .get_engine_type = dal_i2c_hw_engine_get_engine_type, | |
491 | .acquire = dal_i2c_engine_acquire, | |
492 | .submit_request = dal_i2c_hw_engine_submit_request, | |
493 | }; | |
494 | ||
495 | static const struct i2c_hw_engine_funcs i2c_hw_engine_funcs = { | |
496 | .get_hw_buffer_available_size = get_hw_buffer_available_size, | |
497 | .get_transaction_timeout = get_transaction_timeout, | |
498 | .wait_on_operation_result = dal_i2c_hw_engine_wait_on_operation_result, | |
499 | }; | |
500 | ||
b08c3ca4 | 501 | static void construct( |
4562236b HW |
502 | struct i2c_hw_engine_dce110 *hw_engine, |
503 | const struct i2c_hw_engine_dce110_create_arg *arg) | |
504 | { | |
505 | uint32_t xtal_ref_div = 0; | |
506 | ||
b08c3ca4 | 507 | dal_i2c_hw_engine_construct(&hw_engine->base, arg->ctx); |
4562236b HW |
508 | |
509 | hw_engine->base.base.base.funcs = &engine_funcs; | |
510 | hw_engine->base.base.funcs = &i2c_engine_funcs; | |
511 | hw_engine->base.funcs = &i2c_hw_engine_funcs; | |
512 | hw_engine->base.default_speed = arg->default_speed; | |
513 | ||
514 | hw_engine->regs = arg->regs; | |
515 | hw_engine->i2c_shift = arg->i2c_shift; | |
516 | hw_engine->i2c_mask = arg->i2c_mask; | |
517 | ||
518 | hw_engine->engine_id = arg->engine_id; | |
519 | ||
520 | hw_engine->buffer_used_bytes = 0; | |
521 | hw_engine->transaction_count = 0; | |
522 | hw_engine->engine_keep_power_up_count = 1; | |
523 | ||
524 | ||
525 | REG_GET(MICROSECOND_TIME_BASE_DIV, XTAL_REF_DIV, &xtal_ref_div); | |
526 | ||
527 | if (xtal_ref_div == 0) { | |
528 | dm_logger_write( | |
529 | hw_engine->base.base.base.ctx->logger, LOG_WARNING, | |
530 | "Invalid base timer divider\n", | |
531 | __func__); | |
532 | xtal_ref_div = 2; | |
533 | } | |
534 | ||
535 | /*Calculating Reference Clock by divding original frequency by | |
536 | * XTAL_REF_DIV. | |
537 | * At upper level, uint32_t reference_frequency = | |
538 | * dal_i2caux_get_reference_clock(as) >> 1 | |
539 | * which already divided by 2. So we need x2 to get original | |
540 | * reference clock from ppll_info | |
541 | */ | |
542 | hw_engine->reference_frequency = | |
543 | (arg->reference_frequency * 2) / xtal_ref_div; | |
4562236b HW |
544 | } |
545 | ||
546 | struct i2c_engine *dal_i2c_hw_engine_dce110_create( | |
547 | const struct i2c_hw_engine_dce110_create_arg *arg) | |
548 | { | |
549 | struct i2c_hw_engine_dce110 *engine_dce10; | |
550 | ||
551 | if (!arg) { | |
552 | ASSERT_CRITICAL(false); | |
553 | return NULL; | |
554 | } | |
b08c3ca4 DA |
555 | if (!arg->reference_frequency) { |
556 | ASSERT_CRITICAL(false); | |
557 | return NULL; | |
558 | } | |
4562236b | 559 | |
2004f45e HW |
560 | engine_dce10 = kzalloc(sizeof(struct i2c_hw_engine_dce110), |
561 | GFP_KERNEL); | |
4562236b HW |
562 | |
563 | if (!engine_dce10) { | |
564 | ASSERT_CRITICAL(false); | |
565 | return NULL; | |
566 | } | |
567 | ||
b08c3ca4 DA |
568 | construct(engine_dce10, arg); |
569 | return &engine_dce10->base.base; | |
4562236b | 570 | } |