2 * Copyright 2012-15 Advanced Micro Devices, Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
26 #include "dm_services.h"
27 #include "dm_event_log.h"
30 * Pre-requisites: headers required by header of this unit
32 #include "include/i2caux_interface.h"
39 #include "aux_engine.h"
42 * Post-requisites: headers required by this unit
45 #include "include/link_service_types.h"
52 AUX_INVALID_REPLY_RETRY_COUNTER = 1,
53 AUX_TIMED_OUT_RETRY_COUNTER = 2,
54 AUX_DEFER_RETRY_COUNTER = 6
57 #define FROM_ENGINE(ptr) \
58 container_of((ptr), struct aux_engine, base)
60 engine->base.ctx->logger
62 enum i2caux_engine_type dal_aux_engine_get_engine_type(
63 const struct engine *engine)
65 return I2CAUX_ENGINE_TYPE_AUX;
68 bool dal_aux_engine_acquire(
69 struct engine *engine,
72 struct aux_engine *aux_engine = FROM_ENGINE(engine);
74 enum gpio_result result;
75 if (aux_engine->funcs->is_engine_available) {
76 /*check whether SW could use the engine*/
77 if (!aux_engine->funcs->is_engine_available(aux_engine)) {
82 result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE,
83 GPIO_DDC_CONFIG_TYPE_MODE_AUX);
85 if (result != GPIO_RESULT_OK)
88 if (!aux_engine->funcs->acquire_engine(aux_engine)) {
98 struct read_command_context {
100 uint32_t current_read_length;
102 enum i2caux_transaction_status status;
104 struct aux_request_transaction_data request;
105 struct aux_reply_transaction_data reply;
107 uint8_t returned_byte;
109 uint32_t timed_out_retry_aux;
110 uint32_t invalid_reply_retry_aux;
111 uint32_t defer_retry_aux;
112 uint32_t defer_retry_i2c;
113 uint32_t invalid_reply_retry_aux_on_ack;
115 bool transaction_complete;
116 bool operation_succeeded;
119 static void process_read_reply(
120 struct aux_engine *engine,
121 struct read_command_context *ctx)
123 engine->funcs->process_channel_reply(engine, &ctx->reply);
125 switch (ctx->reply.status) {
126 case AUX_TRANSACTION_REPLY_AUX_ACK:
127 ctx->defer_retry_aux = 0;
128 if (ctx->returned_byte > ctx->current_read_length) {
130 I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
131 ctx->operation_succeeded = false;
132 } else if (ctx->returned_byte < ctx->current_read_length) {
133 ctx->current_read_length -= ctx->returned_byte;
135 ctx->offset += ctx->returned_byte;
137 ++ctx->invalid_reply_retry_aux_on_ack;
139 if (ctx->invalid_reply_retry_aux_on_ack >
140 AUX_INVALID_REPLY_RETRY_COUNTER) {
142 I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
143 ctx->operation_succeeded = false;
146 ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
147 ctx->transaction_complete = true;
148 ctx->operation_succeeded = true;
151 case AUX_TRANSACTION_REPLY_AUX_NACK:
152 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
153 ctx->operation_succeeded = false;
155 case AUX_TRANSACTION_REPLY_AUX_DEFER:
156 ++ctx->defer_retry_aux;
158 if (ctx->defer_retry_aux > AUX_DEFER_RETRY_COUNTER) {
159 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
160 ctx->operation_succeeded = false;
163 case AUX_TRANSACTION_REPLY_I2C_DEFER:
164 ctx->defer_retry_aux = 0;
166 ++ctx->defer_retry_i2c;
168 if (ctx->defer_retry_i2c > AUX_DEFER_RETRY_COUNTER) {
169 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
170 ctx->operation_succeeded = false;
173 case AUX_TRANSACTION_REPLY_HPD_DISCON:
174 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON;
175 ctx->operation_succeeded = false;
178 ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
179 ctx->operation_succeeded = false;
183 static void process_read_request(
184 struct aux_engine *engine,
185 struct read_command_context *ctx)
187 enum aux_channel_operation_result operation_result;
189 engine->funcs->submit_channel_request(engine, &ctx->request);
191 operation_result = engine->funcs->get_channel_status(
192 engine, &ctx->returned_byte);
194 switch (operation_result) {
195 case AUX_CHANNEL_OPERATION_SUCCEEDED:
196 if (ctx->returned_byte > ctx->current_read_length) {
198 I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
199 ctx->operation_succeeded = false;
201 ctx->timed_out_retry_aux = 0;
202 ctx->invalid_reply_retry_aux = 0;
204 ctx->reply.length = ctx->returned_byte;
205 ctx->reply.data = ctx->buffer;
207 process_read_reply(engine, ctx);
210 case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY:
211 ++ctx->invalid_reply_retry_aux;
213 if (ctx->invalid_reply_retry_aux >
214 AUX_INVALID_REPLY_RETRY_COUNTER) {
216 I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
217 ctx->operation_succeeded = false;
221 case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT:
222 ++ctx->timed_out_retry_aux;
224 if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) {
225 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
226 ctx->operation_succeeded = false;
228 /* DP 1.2a, table 2-58:
229 * "S3: AUX Request CMD PENDING:
230 * retry 3 times, with 400usec wait on each"
231 * The HW timeout is set to 550usec,
232 * so we should not wait here */
235 case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON:
236 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON;
237 ctx->operation_succeeded = false;
240 ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
241 ctx->operation_succeeded = false;
245 static bool read_command(
246 struct aux_engine *engine,
247 struct i2caux_transaction_request *request,
248 bool middle_of_transaction)
250 struct read_command_context ctx;
252 ctx.buffer = request->payload.data;
253 ctx.current_read_length = request->payload.length;
255 ctx.timed_out_retry_aux = 0;
256 ctx.invalid_reply_retry_aux = 0;
257 ctx.defer_retry_aux = 0;
258 ctx.defer_retry_i2c = 0;
259 ctx.invalid_reply_retry_aux_on_ack = 0;
260 ctx.transaction_complete = false;
261 ctx.operation_succeeded = true;
263 if (request->payload.address_space ==
264 I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
265 ctx.request.type = AUX_TRANSACTION_TYPE_DP;
266 ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_READ;
267 ctx.request.address = request->payload.address;
268 } else if (request->payload.address_space ==
269 I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) {
270 ctx.request.type = AUX_TRANSACTION_TYPE_I2C;
271 ctx.request.action = middle_of_transaction ?
272 I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT :
273 I2CAUX_TRANSACTION_ACTION_I2C_READ;
274 ctx.request.address = request->payload.address >> 1;
276 /* in DAL2, there was no return in such case */
281 ctx.request.delay = 0;
284 memset(ctx.buffer + ctx.offset, 0, ctx.current_read_length);
286 ctx.request.data = ctx.buffer + ctx.offset;
287 ctx.request.length = ctx.current_read_length;
289 process_read_request(engine, &ctx);
291 request->status = ctx.status;
293 if (ctx.operation_succeeded && !ctx.transaction_complete)
294 if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C)
295 msleep(engine->delay);
296 } while (ctx.operation_succeeded && !ctx.transaction_complete);
298 if (request->payload.address_space ==
299 I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
300 DC_LOG_I2C_AUX("READ: addr:0x%x value:0x%x Result:%d",
301 request->payload.address,
302 request->payload.data[0],
303 ctx.operation_succeeded);
306 return ctx.operation_succeeded;
309 struct write_command_context {
313 uint32_t current_write_length;
314 enum i2caux_transaction_status status;
316 struct aux_request_transaction_data request;
317 struct aux_reply_transaction_data reply;
319 uint8_t returned_byte;
321 uint32_t timed_out_retry_aux;
322 uint32_t invalid_reply_retry_aux;
323 uint32_t defer_retry_aux;
324 uint32_t defer_retry_i2c;
325 uint32_t max_defer_retry;
326 uint32_t ack_m_retry;
328 uint8_t reply_data[DEFAULT_AUX_MAX_DATA_SIZE];
330 bool transaction_complete;
331 bool operation_succeeded;
334 static void process_write_reply(
335 struct aux_engine *engine,
336 struct write_command_context *ctx)
338 engine->funcs->process_channel_reply(engine, &ctx->reply);
340 switch (ctx->reply.status) {
341 case AUX_TRANSACTION_REPLY_AUX_ACK:
342 ctx->operation_succeeded = true;
344 if (ctx->returned_byte) {
345 ctx->request.action = ctx->mot ?
346 I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT :
347 I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST;
349 ctx->current_write_length = 0;
353 if (ctx->ack_m_retry > AUX_DEFER_RETRY_COUNTER) {
355 I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
356 ctx->operation_succeeded = false;
360 ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
361 ctx->defer_retry_aux = 0;
362 ctx->ack_m_retry = 0;
363 ctx->transaction_complete = true;
366 case AUX_TRANSACTION_REPLY_AUX_NACK:
367 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
368 ctx->operation_succeeded = false;
370 case AUX_TRANSACTION_REPLY_AUX_DEFER:
371 ++ctx->defer_retry_aux;
373 if (ctx->defer_retry_aux > ctx->max_defer_retry) {
374 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
375 ctx->operation_succeeded = false;
378 case AUX_TRANSACTION_REPLY_I2C_DEFER:
379 ctx->defer_retry_aux = 0;
380 ctx->current_write_length = 0;
382 ctx->request.action = ctx->mot ?
383 I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT :
384 I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST;
386 ++ctx->defer_retry_i2c;
388 if (ctx->defer_retry_i2c > ctx->max_defer_retry) {
389 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
390 ctx->operation_succeeded = false;
393 case AUX_TRANSACTION_REPLY_HPD_DISCON:
394 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON;
395 ctx->operation_succeeded = false;
398 ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
399 ctx->operation_succeeded = false;
403 static void process_write_request(
404 struct aux_engine *engine,
405 struct write_command_context *ctx)
407 enum aux_channel_operation_result operation_result;
409 engine->funcs->submit_channel_request(engine, &ctx->request);
411 operation_result = engine->funcs->get_channel_status(
412 engine, &ctx->returned_byte);
414 switch (operation_result) {
415 case AUX_CHANNEL_OPERATION_SUCCEEDED:
416 ctx->timed_out_retry_aux = 0;
417 ctx->invalid_reply_retry_aux = 0;
419 ctx->reply.length = ctx->returned_byte;
420 ctx->reply.data = ctx->reply_data;
422 process_write_reply(engine, ctx);
424 case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY:
425 ++ctx->invalid_reply_retry_aux;
427 if (ctx->invalid_reply_retry_aux >
428 AUX_INVALID_REPLY_RETRY_COUNTER) {
430 I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
431 ctx->operation_succeeded = false;
435 case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT:
436 ++ctx->timed_out_retry_aux;
438 if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) {
439 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
440 ctx->operation_succeeded = false;
442 /* DP 1.2a, table 2-58:
443 * "S3: AUX Request CMD PENDING:
444 * retry 3 times, with 400usec wait on each"
445 * The HW timeout is set to 550usec,
446 * so we should not wait here */
449 case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON:
450 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON;
451 ctx->operation_succeeded = false;
454 ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
455 ctx->operation_succeeded = false;
459 static bool write_command(
460 struct aux_engine *engine,
461 struct i2caux_transaction_request *request,
462 bool middle_of_transaction)
464 struct write_command_context ctx;
466 ctx.mot = middle_of_transaction;
467 ctx.buffer = request->payload.data;
468 ctx.current_write_length = request->payload.length;
469 ctx.timed_out_retry_aux = 0;
470 ctx.invalid_reply_retry_aux = 0;
471 ctx.defer_retry_aux = 0;
472 ctx.defer_retry_i2c = 0;
474 ctx.transaction_complete = false;
475 ctx.operation_succeeded = true;
477 if (request->payload.address_space ==
478 I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
479 ctx.request.type = AUX_TRANSACTION_TYPE_DP;
480 ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_WRITE;
481 ctx.request.address = request->payload.address;
482 } else if (request->payload.address_space ==
483 I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) {
484 ctx.request.type = AUX_TRANSACTION_TYPE_I2C;
485 ctx.request.action = middle_of_transaction ?
486 I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT :
487 I2CAUX_TRANSACTION_ACTION_I2C_WRITE;
488 ctx.request.address = request->payload.address >> 1;
490 /* in DAL2, there was no return in such case */
495 ctx.request.delay = 0;
497 ctx.max_defer_retry =
498 (engine->max_defer_write_retry > AUX_DEFER_RETRY_COUNTER) ?
499 engine->max_defer_write_retry : AUX_DEFER_RETRY_COUNTER;
502 ctx.request.data = ctx.buffer;
503 ctx.request.length = ctx.current_write_length;
505 process_write_request(engine, &ctx);
507 request->status = ctx.status;
509 if (ctx.operation_succeeded && !ctx.transaction_complete)
510 if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C)
511 msleep(engine->delay);
512 } while (ctx.operation_succeeded && !ctx.transaction_complete);
514 if (request->payload.address_space ==
515 I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
516 DC_LOG_I2C_AUX("WRITE: addr:0x%x value:0x%x Result:%d",
517 request->payload.address,
518 request->payload.data[0],
519 ctx.operation_succeeded);
522 return ctx.operation_succeeded;
525 static bool end_of_transaction_command(
526 struct aux_engine *engine,
527 struct i2caux_transaction_request *request)
529 struct i2caux_transaction_request dummy_request;
532 /* [tcheng] We only need to send the stop (read with MOT = 0)
533 * for I2C-over-Aux, not native AUX */
535 if (request->payload.address_space !=
536 I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C)
539 dummy_request.operation = request->operation;
540 dummy_request.payload.address_space = request->payload.address_space;
541 dummy_request.payload.address = request->payload.address;
544 * Add a dummy byte due to some receiver quirk
545 * where one byte is sent along with MOT = 0.
546 * Ideally this should be 0.
549 dummy_request.payload.length = 0;
550 dummy_request.payload.data = &dummy_data;
552 if (request->operation == I2CAUX_TRANSACTION_READ)
553 return read_command(engine, &dummy_request, false);
555 return write_command(engine, &dummy_request, false);
557 /* according Syed, it does not need now DoDummyMOT */
560 bool dal_aux_engine_submit_request(
561 struct engine *engine,
562 struct i2caux_transaction_request *request,
563 bool middle_of_transaction)
565 struct aux_engine *aux_engine = FROM_ENGINE(engine);
568 bool mot_used = true;
570 switch (request->operation) {
571 case I2CAUX_TRANSACTION_READ:
572 result = read_command(aux_engine, request, mot_used);
574 case I2CAUX_TRANSACTION_WRITE:
575 result = write_command(aux_engine, request, mot_used);
582 * need to send stop for the last transaction to free up the AUX
583 * if the above command fails, this would be the last transaction */
585 if (!middle_of_transaction || !result)
586 end_of_transaction_command(aux_engine, request);
588 /* mask AUX interrupt */
593 void dal_aux_engine_construct(
594 struct aux_engine *engine,
595 struct dc_context *ctx)
597 dal_i2caux_construct_engine(&engine->base, ctx);
599 engine->max_defer_write_retry = 0;
602 void dal_aux_engine_destruct(
603 struct aux_engine *engine)
605 dal_i2caux_destruct_engine(&engine->base);